Thread的老管家ThreadGroup
大家好我是kconn,我是一個不愛看原始碼,不喜歡分析原始碼,更不喜歡寫文章的程式員。自從面試被人虐後,我改過自新,打算重新做人。不是,是重新做猿。啊呸,是好好學習,天天肛原始碼。

蝸殼鎮樓!!!
前段時候看Thread的時候,有點想寫篇關於他的文章,但是發現裡面的內容有點多,於是就打算拆出好幾份來寫。今天我要肛的物件就是ThreadGroup,翻譯過來就是“執行緒組”,也就是執行緒的老管家。
好,廢話不多說,我們來肛原始碼!
點開ThreadGroup類。
// 系統執行緒組 static final ThreadGroup systemThreadGroup = new ThreadGroup(); // 主執行緒組 static final ThreadGroup mainThreadGroup = new ThreadGroup(systemThreadGroup, "main"); // 父執行緒組 private final ThreadGroup parent; // 執行緒組裡面的執行緒組陣列,也就是子執行緒組陣列 ThreadGroup groups[]; // 執行緒陣列 Thread threads[]; ... /** * 系統執行緒組的專屬 */ private ThreadGroup() { this.name = "system"; // 優先順序,內定的最強王者 this.maxPriority = Thread.MAX_PRIORITY; // 系統就是最大的了,所以父執行緒組為null this.parent = null; } public ThreadGroup(String name) { // 太吾繪卷核心思想——薪火相傳 this(Thread.currentThread().getThreadGroup(), name); } public ThreadGroup(ThreadGroup parent, String name) { // checkParentAccess按資料顯示是判定當前執行的執行緒是否有權修改該執行緒 this(checkParentAccess(parent), parent, name); } /** * 這裡要說下Void是void的包裝類,和Integer是int的包裝類一樣。 */ private ThreadGroup(Void unused, ThreadGroup parent, String name) { // 執行緒組名 this.name = name; // 優先順序 this.maxPriority = parent.maxPriority; // 是否是守護執行緒或使用者執行緒 this.daemon = parent.daemon; // boolean型別,沒參與任何判斷,沒看出有什麼作用。資料顯示是否可中斷 this.vmAllowSuspension = parent.vmAllowSuspension; this.parent = parent; // 子執行緒組加入父執行緒組 parent.add(this); }
好了,飯要一口一口的吃,先讓我們來看下checkParentAccess是怎麼判斷的
private static Void checkParentAccess(ThreadGroup parent) { parent.checkAccess(); return null; } public final void checkAccess() { // 裡面真的什麼都沒有 }
checkAccess是個空方法,我瞬間懵逼了,翻了下前面幾個版本的原始碼,裡面都是空空如也。不知道這麼做的意義是什麼。為了父執行緒組為空的時候拋異常?也就是說,只要他不為空就有權修改該執行緒?好吧,這問題就放下不表。

好了,接著看add方法。
private final void add(ThreadGroup g){ synchronized (this) { // 如果該執行緒組銷燬了就拋異常,沒毛病 if (destroyed) { throw new IllegalThreadStateException(); } // 為空就建立 // 不為空並且超過了就擴容 if (groups == null) { groups = new ThreadGroup[4]; } else if (ngroups == groups.length) { groups = Arrays.copyOf(groups, ngroups * 2); } // 子執行緒組加入,成為馬仔 groups[ngroups] = g; ngroups++; } }
好了,以上就是本篇的全部內容。

咳咳,不皮了,我們剛剛看完了他的構造方法,接下來看看在Thread裡面他是怎麼運用的。首先先找Thread裡使用的場景。
private void init(ThreadGroup g, Runnable target, String name, long stackSize) { // 如果執行緒組為空就採用當前執行緒的執行緒組 Thread parent = currentThread(); if (g == null) { g = parent.getThreadGroup(); } // 未開始執行的執行緒數+1 g.addUnstarted(); ... }
接著點進去看addUnstarted方法。
void addUnstarted() { synchronized(this) { if (destroyed) { throw new IllegalThreadStateException(); } // 未開始執行的執行緒數+1 nUnstartedThreads++; } }
也就是在初始化Thread的時候,未開始執行的執行緒+1,這也很好理解,我們在Thread沒start的時候也就代表這個執行緒還未開始執行。
public synchronized void start() { ... // 執行緒組加入執行緒 group.add(this); started = false; try { ... } finally { try { if (!started) { // 從執行緒中移除當前執行緒,未啟動執行緒數+1 group.threadStartFailed(this); } } catch (Throwable ignore) { } } }
這裡要說明下,執行緒組裡面有兩個add方法,一個是add子執行緒組,另一個就是add執行緒。
void add(Thread t) { synchronized (this) { // 邏輯和執行緒組陣列的邏輯一模一樣 if (destroyed) { throw new IllegalThreadStateException(); } if (threads == null) { threads = new Thread[4]; } else if (nthreads == threads.length) { threads = Arrays.copyOf(threads, nthreads * 2); } // 執行緒加入 threads[nthreads] = t; nthreads++; // 未啟動執行緒數-1 nUnstartedThreads--; } }
仔細的我發現,add執行緒組的方法是private的,而add執行緒的方法則是預設的friendly。nUnstartedThreads-1也代表了Thread開始的時候,那麼代表這個執行緒開始執行了。

好吧,接著往下看。
/** * 執行緒啟動失敗 */ void threadStartFailed(Thread t) { synchronized(this) { // 移除執行緒 remove(t); // 未啟動執行緒數+1 nUnstartedThreads++; } }
看下remove方法。
private void remove(Thread t) { synchronized (this) { // 如果已經銷燬,不執行後面程式碼 if (destroyed) { return; } for (int i = 0 ; i < nthreads ; i++) { if (threads[i] == t) { // copy陣列 System.arraycopy(threads, i + 1, threads, i, --nthreads - i); // 幹掉執行緒 threads[nthreads] = null; break; } } } }
好了,Thread的start場景都看過了,現在回過頭看Thread的exit方法。
private void exit() { if (group != null) { // 移除執行緒 group.threadTerminated(this); // 老管家退休 group = null; } ... }
進入到ThreadGroup的threadTerminated方法。
void threadTerminated(Thread t) { synchronized (this) { // 移除執行緒 remove(t); if (nthreads == 0) { notifyAll(); } // 滿足以下條件就銷燬 if (daemon && (nthreads == 0) && (nUnstartedThreads == 0) && (ngroups == 0)) { destroy(); } } }
destroy方法也沒啥好說的,看下就行了。

public final void destroy() { int ngroupsSnapshot; ThreadGroup[] groupsSnapshot; synchronized (this) { checkAccess(); if (destroyed || (nthreads > 0)) { throw new IllegalThreadStateException(); } ngroupsSnapshot = ngroups; if (groups != null) { groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); } else { groupsSnapshot = null; } if (parent != null) { destroyed = true; ngroups = 0; groups = null; nthreads = 0; threads = null; } } for (int i = 0 ; i < ngroupsSnapshot ; i += 1) { groupsSnapshot[i].destroy(); } if (parent != null) { // 父執行緒組移除子執行緒組 parent.remove(this); } }
前面看著有兩個add,這裡也發現有兩個remove。
private void remove(ThreadGroup g) { synchronized (this) { // 移除的邏輯都差不多 if (destroyed) { return; } for (int i = 0 ; i < ngroups ; i++) { if (groups[i] == g) { ngroups -= 1; System.arraycopy(groups, i + 1, groups, i, ngroups - i); groups[ngroups] = null; break; } } if (nthreads == 0) { notifyAll(); } if (daemon && (nthreads == 0) && (nUnstartedThreads == 0) && (ngroups == 0)) { destroy(); } } }
以上就是Thread運用到ThreadGroup的場景做一次比較簡單的原始碼閱讀。

最後再說一句,肛原始碼不容易,我也不是大牛,文章也寫的不是很好。大家看著有什麼想法都可以說,我不一定會看,看了也不一定會回,因為我懶!

bye~