# 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 保證執行緒編號的不重複