一.線程組
實際上,線程組是一個設計失敗的概念。它最初是出於安全的考慮被設計用來隔離不同的applet(已過時)的。然而,ThreadGroup並未實現這一預期目標,並且它所實現的許多方法是有缺陷的。但是ThreadGroup仍然遺留在其他和線程有關的類中,因此這裏只是對它進行一個簡單的介紹。在大多數情況下,我們完全可以忽略線程組這一概念以及它的存在。
一個線程組代表了一組線程。此外,一個線程組也能包含其他的線程組。線程與線程組的關係類似於文件和文件夾的關係,即一個文件總是位於特定的文件夾中,而一個文件夾可以包含多個文件和文件夾。通過線程組可以很方便的對多個線程進行管理。下圖展示了線程組與線程及其他線程組的關係:
上面的線程和線程組組成了一棵樹,除了根線程組外,其他每個線程組都有一個父線程組。
Thread類有幾個構造器允許我們在創建線程的時候指定其所屬的線程組。如果在創建線程的時候沒有指定線程組,那麼這個線程就屬於其父線程所屬的線程組。因此,每一個線程都一個線程組與之關聯。
1.成員變量
Java中使用ThreadGroup類來表示線程組。先來看這個類有哪些成員變量:
- parent:父線程組;
- name:線程組名稱;
- maxPriority:最大優先級。該線程組中的每個線程的優先級都不能超過這個值。
- destroyed:該線程組是否已經被銷燬。
- daemon:是否爲守護線程組。線程組是否爲守護線程組與其管理的線程是否爲守護線程沒有關係,但概念類似。即若某線程組爲守護線程組,則當該線程組的最後一個線程被停止或最後一個線程組被銷燬後,該線程組將會自動銷燬。
- nUnstartedThreads:線程組中未啓動的線程個數。
- nthreads:線程組中已啓動的線程個數,不包含子線程組中的線程;
- threads:用來存儲線程組中線程的數組,不包含未啓動的線程;
- ngroups:線程組中子線程組的個數,不包含子線程組中的線程組;
- groups:用來存儲線程組中子線程組的數組。
2.構造方法
接着繼續來看ThreadGroup的構造方法:
public ThreadGroup(String name) {
this(Thread.currentThread().getThreadGroup(), name);
}
public ThreadGroup(ThreadGroup parent, String name) {
this(checkParentAccess(parent), parent, name);
}
private ThreadGroup(Void unused, ThreadGroup parent, String name) {
this.name = name;
this.maxPriority = parent.maxPriority;
this.daemon = parent.daemon;
this.parent = parent;
parent.add(this);
}
ThreadGroup有兩個public的構造方法可以供我們使用,其中ThreadGroup(ThreadGroup parent, String name)可以指定父線程組,而ThreadGroup(String name)則默認以當前線程所在的線程組作爲父線程組。
實際上,ThreadGroup還有一個無參的構造方法:
/**
* Creates an empty Thread group that is not in any Thread group.
* This method is used to create the system Thread group.
*/
private ThreadGroup() { // called from C code
this.name = "system";
this.maxPriority = Thread.MAX_PRIORITY;
this.parent = null;
}
這個構造方法用於創建系統線程組,它會在JVM啓動的時候被底層C語言代碼來調用。系統線程組是所有線程組的祖先,它沒有父線程組。
3.成員方法
下面介紹ThreadGroup的幾個成員方法:
- int activeCount()
返回線程組和子線程組中活躍的線程數。這是一個估值,因爲在遍歷線程的過程中,有些線程可能已經停止。
- int activeGroupCount()
返回線程組和子線程組中活躍的線程組數。線程組只有兩種狀態,active和destroyed,因此未被銷燬的線程組都是活躍的線程組。這也是一個估值,因爲在遍歷線程組的過程中,有些線程組可能已經被銷燬。
- void destroy()
銷燬當前線程組。若線程組已被銷燬或包含活躍的線程時該方法將會拋出異常。
- int enumerate(Thread[] list)
將該線程組及子線程組中活躍的線程拷貝至指定的數組。
- int enumerate(Thread[] list, boolean recurse)
將該線程組及子線程組(可選,recurse爲false時只拷貝該線程組中活躍的線程)中活躍的線程拷貝至指定的數組。
- int enumerate(ThreadGroup[] list)
將該線程組及子線程組中活躍的線程組拷貝至指定的數組。
- int enumerate(ThreadGroup[] list, boolean recurse)
將該線程組及子線程組(可選,recurse爲false時只拷貝該線程組中活躍的線程組)中活躍的線程組拷貝至指定的數組。
- void interrupt()
中斷線程組中所有的線程。
二.線程工廠
ThreadFactory接口是Java中的線程工廠,它是工廠模式的一種體現。它定義瞭如下工廠方法:
public Thread newThread(Runnable r);
通過自定義線程工廠,我們可以在創建線程時對線程進行一些設置,例如是否是守護線程,統一給線程命名,設置線程優先級,設置線程組等,這使得在創建多個線程時無需手動調用Thread類的構造方法。例如Executors的newFixedThreadPoll(int nThreads, ThreadFactory threadFactory)方法,該方法會返回一個包含固定數量線程的線程池(有關線程池的內容會在後面的文章中介紹),我們可以將線程工廠傳遞給這個方法,這樣線程池在創建線程時就會通過我們的線程工廠的方法去創建。
例如,下面的線程工廠爲每一個線程都設置了統一格式的名字:
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class MyThreadFactory implements ThreadFactory {
private static final String THREAD_NAME_PREFIX = "mythread-";
private static final AtomicInteger THREAD_NUMBER = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, THREAD_NAME_PREFIX + THREAD_NUMBER.getAndIncrement());
}
}