# JUC源码讲解:逐步解析 Thread.init() 源码
抛出问题
我们在 new Thread() 时,init() 方法便会自动调用,用来创建这个线程。那么,创建线程时都发生了什么事?子线程与父线程有何关系?线程是怎么创建的?juc怎么选择 ThreadGroup? 让我们从源码中寻找答案吧!
查看源码
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
可以看到,init() 传递了 ThreadGroup(划重点),以及 name(线程名),然后调用了重名方法 init()。
为了方便大家学习,我先把这个init()的代码贴出来,然后带领大家逐步分析源码
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
if (security != null) {
g = security.getThreadGroup();
}
if (g == null) {
g = parent.getThreadGroup();
}
}
g.checkAccess();
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize;
tid = nextThreadID();
}
我们先看第一段代码:
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
每一个线程都必须要有一个名字,创建时会自动起名,这里进行了反复的检查。
建议大家在写项目的时候都为线程自定义线程名,以好使用 stack log 查日志
再看下一段:
Thread parent = currentThread();
这里是找到父线程,简单地说,如果我们是在 main 方法中调用的 new Thread(), 那么 main 线程就是这个 parent
接着看下一段代码,这段代码是保证了 ThreadGroup 的可用
// 先找到系统的 ThreadGroup
SecurityManager security = System.getSecurityManager();
// 尊重用户传递的 ThreadGroup
if (g == null) {
if (security != null) {
// 用户没有传递,就有限使用系统的 ThreadGroup
g = security.getThreadGroup();
}
if (g == null) {
// 连系统的都找不到,就使用父线程的 ThreadGroup
g = parent.getThreadGroup();
}
}
这里,是代码对 ThreadGroup 的反复确认,如果 ThreadGroup 为空,并且我们找到了系统级别的 ThreadGroup,那就使用系统级别的 ThreadGroup,如果系统级的 ThreadGroup 也没有找到,就使用父线程的ThreadGroup。
这里可以看到优先级了,juc 尊重用户的 ThreadGroup,次选System的,最后才选用 parent 的
看下一段,检查线程可用,不必多说
g.checkAccess();
看下一段,这里的线程还不是开启的状态,NEW 状态的线程!,还没有 start,添加到 ThreadGroup中
g.addUnstarted(); // NEW
接着,我们看这一段核心的代码:
this.daemon = parent.isDaemon(); // 继承父线程的守护线程
this.priority = parent.getPriority(); // 继承父线程的优先级
// 击沉父线程的 contextClassLoader
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
// 尽可能的继承父线程的 inheritableThreadLocals
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
子线程与父线程的关系在这段代码中昭然若揭,子线程会继承父线程的 daemon 、 priority 、contextClassLoader,会尽可能的继承父线程的 inheritableThreadLocals
最后一段代码,设置线程编号,收尾!
tid = nextThreadID();
我们点进去这个函数看一看:
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
看到了吗,是sync的,juc 是这样保证线程编号的一致性的
总结
通过观察 Thread.init() 的源码,我们可以主要了解到:
- ThreadGroup:JUC会尊重用户指定的ThreadGroup,次选 System 的,最后会选择父线程的
- 子线程与父线程的继承关系:子线程会完全继承父线程的 daemon 、 priority 、contextClassLoader,会尽可能的继承父线程的 inheritableThreadLocals
- Thread.init() 时的线程状态是 NEW,会被添加到 ThreadGroup 中
- init() 时通过 sync 保证线程编号的不重复