1.ThreadGroup詳解,直接看示例代碼,注釋非常詳細
package com.landon.mavs.example.concurrent;

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/** *//**
*
* {@link java.lang.ThreadGroup}示例
*
* <pre>
* 1.線程組表示一個線程的結合.此外線程組也可以包含其他線程組.線程組構成一棵樹,在樹中,除了初始線程組外,每個線程組都有一個父線程組
* 2.每個線程產生時,都會被歸入某個線程組(Java中每個線程都是屬于某個線程組),視線程是在那個線程組中產生而定.如果沒有指定,則歸入產生該子線程的線程的線程組中.(如在main中初始化一個線程,未指定線程組,則線程所屬線程組為main)
* 3.線程一旦歸入某個組就無法更換組
* 4.main線程組的parent是system線程組,而system線程組的parent為null.(參考ThreadGroup的私有構造方法).也就是說初始線程組為system.以system/main衍生出一顆樹
* 5.其activeCount/activeCount/enumerate方法均為不精確的統計,建議僅用于信息目的
* 6.可通過enumerate獲得活動線程的引用并對其進行操作
* 7.允許線程訪問有關自己的線程組的信息{@link Thread#getThreadGroup()},但是不允許它訪問有關其線程組的父線程組或其他任何線程組的信息
* 8.線程組的某些方法,將對線程組機器所有子組的所有線程執行,如{@link ThreadGroup#interrupt()}
* 7.public class ThreadGroup implements Thread.UncaughtExceptionHandler,即其實現了UncaughtExceptionHandler方法.即
* ->當Thread因未捕獲的異常而突然中止時,調用處理程序的接口.當某一線程因捕獲的異常而即將中止時,JVM將使用UncaughtExceptionHandler查詢該線程以獲得其
* UncaughtExceptionHandler的線程并調用處理程序的uncaughtException方法,將線程和異常作為參數傳遞.如果某一線程為明確設置其UncaughtExceptionHandler,
* 則將它的ThreadGroup對象作為UncaughtExceptionHandler.如果ThreadGroup對象對處理異常沒有特殊要求,可以將調用轉發給
* {@link Thread#getDefaultUncaughtExceptionHandler()}
* </pre>
*
* <pre>
* 1."線程是獨立執行的代碼片斷,線程的問題應該由線程自己來解決,而不要委托到外部".基于這樣的設計理念,在Java中,線程方法的異常
* (無論是checked還是unchecked exception),都應該在線程代碼邊界之內(run方法內)進行try catch并處理掉
* .換句話說,我們不能捕獲從線程中逃逸的異常
*
* 2.參考{@link Thread#dispatchUncaughtException},該方法是一個私有方法,在異常逃逸時調用.判斷線程自身是否設置了uncaughtExceptionHandler.
* 如果沒有則直接返回group,即自己的所在的線程組,而線程組實現了UncaughtExceptionHandler接口.{@link Thread#getUncaughtExceptionHandler()}
* </pre>
*
* @author landon
*
*/

public class ThreadGroupExample
{
private static final Logger LOGGER = LoggerFactory
.getLogger(ThreadGroupExample.class);


public static void main(String[] args)
{
// main.thread.name:main,main.threadGroup.name:main
// 即當前線程為main,其所屬線程組
// Thread#public final ThreadGroup getThreadGroup()
// 返回該線程所屬的線程組.如果該線程已經停止運行則返回null
LOGGER.debug("main.thread.name:{},main.threadGroup.name:{}", Thread
.currentThread().getName(), Thread.currentThread()
.getThreadGroup().getName());

Thread thread1 = new Thread("Thread-1");
// 從輸出可以看到
// 1.thread1為指定線程組,則其輸出的線程組為main,即產生thread1的main線程所屬的線程組main
// 2.thread1并未啟動.其所屬線程組就已經指定了,即在線程初始化的時候就已經擁有了
// 從源碼看:
// Thread#private void init(ThreadGroup g, Runnable target,String
// name,long stackSize)
// 1.Thread parent = currentThread(),獲取調用線程,即產生thread的線程
// 2.判斷SecurityManager是否為空,如果不為空,則用SecurityManager#getThreadGroup().
// 3.如果SecurityManager#getThreadGroup()為空或者不存在SecurityManager,則線程組賦值為parent.getThreadGroup(),即調用線程所屬的線程組
// 另外從Thread的初始化方法中可以看到,線程是否是守護線程以及線程的優先級均是由parent指定
LOGGER.debug("thread1.name:{},thread1.threadGroup.name:{}",
thread1.getName(), thread1.getThreadGroup().getName());
// 從是否是守護線程和線程的優先級的輸出來看,threa1和其parent線程main的是一樣的
LOGGER.debug("threa1.name:{},threa1.isDaemon:{},threa1.priority:{}",
thread1.getName(), thread1.isDaemon(), thread1.getPriority());
Thread curThread = Thread.currentThread();
LOGGER.debug(
"curThread.name:{},curThread.isDaemon:{},curThread.priority:{}",
curThread.getName(), curThread.isDaemon(),
curThread.getPriority());

// 該線程未執行名字,從輸出看:其名字是Thread-0
// 從Thread的初始化方法可以看到,當名字為null的時候(即匿名線程),會指定一個名字"Thread-" +
// nextThreadNum()
// 而nextThreadNum是一個靜態同步方法,對threadInitNumber這個靜態計數器執行++
Thread thread2 = new Thread();
LOGGER.debug("threa2.name:{}", thread2.getName());

// public ThreadGroup(String name) 構造一個新的線程組,該新線程組的父線程組是目前調用線程的線程組
// 從源碼上看:
// 1.this(Thread.currentThread().getThreadGroup(),
// name),即將當前調用線程所屬的線程組作為其父parent線程組傳入
// 2.將parent.maxPriority/daemon賦值傳入
// 3.parent.add(this),將將該線程組加入到父線程組
// 4.另外ThreadGroup有一個私有的空參數的構造方法,其指定線程組名字為system,priority為最大優先級,parent為null,即沒有父線程組.從該代碼的注釋來看,
// ->該私有構造方法通常用來創建一個系統線程組,C代碼調用
ThreadGroup group1 = new ThreadGroup("ThreadGroup-1");
// 輸出:group1.parent.name:main,即group1的父線程組為main
// ThreadGroup#public final ThreadGroup getParent() 返回線程組的父線程組
LOGGER.debug("group1.parent.name:{}", group1.getParent().getName());
ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
// 輸出:mainGroup.parentOf(group1):true,group1.parentOf(group1),true
// ThreadGroup#public final boolean parentOf(ThreadGroup g)
// 判斷該線程組是否為線程組參數或者是其祖先線程組
// 從輸出看,mainGroup是group1的parent,而傳入原組,方法也返回true
LOGGER.debug(
"mainGroup.parentOf(group1):{},group1.parentOf(group1),{}",
mainGroup.parentOf(group1), group1.parentOf(group1));
// 從輸出看mainGroup的父線程組為system.參考ThreadGroup的私有構造方法,可知system是一個系統線程組,其parent為null.即為初始線程組
LOGGER.debug("mainGroup.parent.name:{}", mainGroup.getParent()
.getName());

GroupExampleThread get1 = new GroupExampleThread("GroupExampleThread-1");
// 從Thread#start源碼看,在啟動線程后->會調用group.add(this),即只有在線程啟動后,將才會將線程加入其所屬線程組
// 另外注意ThreadGroup#void add(Thread
// t),即該方法只有默認訪問權限,即包訪問權限.所以應用程序無法調用該方法除非jdk lang包內的庫
get1.start();
// ThreadGroup#public int activeCount()
// 返回此線程組中活動線程的估計數,結果并不能反映并發活動(因為多線程并發運行,所以不是很精確.多線程的不確定性,如add(某一新增線程啟動)/remove(某一現有線程銷毀)).
// ->固有的不精確性->建議只用于信息
// 從源碼的實現看,其計算數目只是取了一個groupsSnapshot(syncrhonized),即當前的快照
LOGGER.debug("mainGroup.activeCount:{}", get1.getThreadGroup()
.activeCount());
Thread[] mainGroupActiveThreads = new Thread[mainGroup.activeCount()];
// ThreadGroup#public int enumerate(Thread list[])
// 將此線程組即其子組中的所有活動線程復制到指定數組中.注應用程序可用使用activeCount方法獲取數組大小的估計數.
// 如果數組太小而無法保持所有線程,則忽略額外的線程
// 可額外校驗該方法的返回值是否嚴格小于參數list的長度
// 因為此方法固有的競爭條件(源碼實現也是取了一個Snapshot(syncrhonized)),建議僅用于信息目的
mainGroup.enumerate(mainGroupActiveThreads);
// 從輸出看:1.主線程組包括兩個活動線程,main和GroupExampleThread-1
// 2.Thread#toString方法返回的信息是Thread[threadName,priority,groupName(如果其group不為null)]
LOGGER.debug("mainGroupActiveThreads:{}",
Arrays.toString(mainGroupActiveThreads));

// Thread#public Thread(ThreadGroup group, String name) 指定線程組
GroupExampleThread get2 = new GroupExampleThread(group1,
"GroupExampleThread-2");
// 通過調用start將get2加入group1
get2.start();

// 輸出:group1.activeCount:1
LOGGER.debug("group1.activeCount:{}", group1.activeCount());
// 輸出:mainGroup.activeCount:3,即其統計包括子線程的活動線程數目,因為group1為其子組
LOGGER.debug("mainGroup.activeCount:{}", mainGroup.activeCount());

Thread[] mainGroupActiveThreads2 = new Thread[mainGroup.activeCount()];
// ThreadGroup#public int enumerate(Thread list[], boolean recurse)
// recurse為遞歸的意思
// 如果recurse為true表示要復制遍歷子線程組的活動線程,否則只是復制當前線程組的活動線程
// enumerate(Thread list[])方法默認傳true
mainGroup.enumerate(mainGroupActiveThreads2, false);
// 輸出:[Thread[main,5,main], Thread[GroupExampleThread-1,5,main], null]
// 從輸出可以看,只包括主線程組自身的活動線程
LOGGER.debug("mainGroupActiveThreads2:{}",
Arrays.toString(mainGroupActiveThreads2));

Thread[] mainGroupActiveThreads3 = new Thread[mainGroup.activeCount()];
mainGroup.enumerate(mainGroupActiveThreads3, true);
// 輸出:Thread[GroupExampleThread-1,5,main],
// Thread[GroupExampleThread-2,5,ThreadGroup-1]]
// 從輸出可以看,包含了主線程組的子線程組group1的活動線程
LOGGER.debug("mainGroupActiveThreads3:{}",
Arrays.toString(mainGroupActiveThreads3));

// 通過enumerate方法得到活動線程的引用,我們可以對其進行操作
Thread get1Ref = mainGroupActiveThreads3[1];
LOGGER.debug("get1 == get1Ref:{}", get1 == get1Ref);
// 中斷get1,從輸出看,get1線程任務確實被打斷了
get1Ref.interrupt();

// 可以遍歷活動線程列表,進行操作如獲取線程狀態,判斷是否處于活動狀態等

for (Thread tRef : mainGroupActiveThreads3)
{
LOGGER.debug("threadName:{},state:{},isAlive:{}", tRef.getName(),
tRef.getState(), tRef.isAlive());
}

// ThreadGroup#public final void interrupt() 對線程組及其子組中的所有線程調用interrupt方法
// 從輸出可以看到get2也被中斷了
mainGroup.interrupt();

// 自定義的一個group,覆寫了uncaughtException方法i
ExampleThreadGroup etg = new ExampleThreadGroup("ExampleThreadGroup");
AExceptionThread aet = new AExceptionThread(etg, "A-Exception-Thread");
aet.start();

// ThreadGroup#public void list() 將有關此線程組的信息打印的標準輸出.此方法只對調試有用
// 包括此線程組的信息:className[name=groupName,maxPri=],還包括當前組內線程的信息,{@link
// Thread#toString()}.{@link ThreadGroup#toString()}
// 然后迭代子線程組進行輸出
etg.list();
mainGroup.list();
}


/** *//**
*
* GroupExampleThread
*
* @author landon
*
*/

private static class GroupExampleThread extends Thread
{

public GroupExampleThread(String name)
{
super(name);
}

// 此構造方法指定了線程組

public GroupExampleThread(ThreadGroup tg, String name)
{
super(tg, name);
}

@Override

public void run()
{
LOGGER.debug("GroupExampleThread" + "[" + getName() + "]"
+ " task begin.");

// 用sleep模擬任務耗時

try
{
sleep(5 * 1000);

} catch (InterruptedException e)
{
LOGGER.debug("GroupExampleThread" + "[" + getName() + "]"
+ " was interrutped");
}

LOGGER.debug("GroupExampleThread" + "[" + getName() + "]"
+ " task end.");
}
}

// 自實現的一個線程組,覆寫了uncaughtException

private static class ExampleThreadGroup extends ThreadGroup
{


public ExampleThreadGroup(String name)
{
super(name);
}

@Override

public void uncaughtException(Thread t, Throwable e)
{
// 注.該方法調用是在t線程,參考Thread#dispatchUncaughtException,是一個私有方法
LOGGER.debug("uncaughtException.thread.name.{}", t.getName(), e);
LOGGER.debug("uncaughtException.thread.state.{}", t.getState());

// 這里再重新啟動一個新線程
GroupExampleThread thread = new GroupExampleThread(this,
t.getName());
thread.start();
}
}


private static class AExceptionThread extends Thread
{

public AExceptionThread(ThreadGroup group, String name)
{
super(group, name);
}

@Override

public void run()
{
// 直接拋出一個空指針異常
throw new NullPointerException();
}
}
}

2.總結:本篇用示例代碼并結合jdk源碼詳細講解了ThreadGroup.
posted on 2013-12-18 18:03
landon 閱讀(2394)
評論(0) 編輯 收藏 所屬分類:
Program