Java多執行緒16:執行緒組

五月的倉頡發表於2015-10-05

執行緒組

可以把執行緒歸屬到某一個執行緒組中,執行緒組中可以有執行緒物件,也可以有執行緒組,組中還可以有執行緒,這樣的組織結構有點類似於樹的形式,如圖所示:

執行緒組的作用是:可以批量管理執行緒或執行緒組物件,有效地對執行緒或執行緒組物件進行組織

 

執行緒關聯執行緒組:1級關聯

所謂1級關聯就是父物件中有子物件,但並不建立孫物件。這種情況在開發中很常見,比如建立一些執行緒時,為了有效對這些執行緒進行阻止管理,通常情況下是建立一個執行緒組,然後再將部分執行緒歸屬到該組中,以此來對零散的執行緒物件進行有效的管理。

看一下簡單的1級關聯的例子:

public class MyThread49 implements Runnable
{
    public void run()
    {
        try
        {
            while (!Thread.currentThread().isInterrupted())
            {
                System.out.println("ThreadName = " + Thread.currentThread().getName());
                Thread.sleep(3000);
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}
public static void main(String[] args)
{
    MyThread49 mt0 = new MyThread49();
    MyThread49 mt1 = new MyThread49();
    ThreadGroup tg = new ThreadGroup("新建執行緒組1");
    Thread t0 = new Thread(tg, mt0);
    Thread t1 = new Thread(tg, mt1);
    t0.start();
    t1.start();
    System.out.println("活動的執行緒數為:" + tg.activeCount());
    System.out.println("執行緒組的名稱為:" + tg.getName());
}

看一下執行結果:

活動的執行緒數為:2
ThreadName = Thread-1
ThreadName = Thread-0
執行緒組的名稱為:新建執行緒組1
ThreadName = Thread-1
ThreadName = Thread-0
ThreadName = Thread-1
ThreadName = Thread-0
ThreadName = Thread-1
ThreadName = Thread-0
...

控制檯上列印出的資訊表示執行緒組中有兩個執行緒,並且列印出了執行緒組的名稱。另外,兩個執行緒無限隔3秒列印,也符合程式碼預期

 

執行緒關聯執行緒組:多級關聯

所謂的多級關聯就是父物件中有子物件,子物件中再建立子物件買也就出現了子孫的效果了。但是這種寫法在開發中不太常見,因為執行緒樹如果涉及得複雜反而不利於執行緒物件的管理,不過JDK確實提供了多級關聯的執行緒樹結構。

多級關聯的程式碼就不寫了,簡單看一下怎麼使用關機關聯,檢視下JDK API的ThreadGroup構造方法:

注意一下第二個,假如要使用多級關聯一般就是用第二個建構函式。第一個參數列示新執行緒組的父執行緒組,第二個參數列示新執行緒組的名稱,有了父執行緒組和新執行緒組的名稱,自然可以構造出一個新的執行緒組來了。

當然用第一個構造方法也是可以的,下一部分就會提到。

另外注意一點,執行緒必須啟動後才能歸到指定執行緒組中

 

執行緒組自動歸屬特性

自動歸屬的意思就是自動歸到當前執行緒組中,看一下例子:

public static void main(String[] args)
{
    System.out.println("A處執行緒:" + Thread.currentThread().getName() + ", 所屬執行緒:" + Thread.currentThread().getThreadGroup().getName() + 
        ", 組中有執行緒組數量:" + Thread.currentThread().getThreadGroup().activeGroupCount());
    ThreadGroup group = new ThreadGroup("新的組");
    System.out.println("B處執行緒:" + Thread.currentThread().getName() + ", 所屬執行緒:" + Thread.currentThread().getThreadGroup().getName() + 
        ", 組中有執行緒組數量:" + Thread.currentThread().getThreadGroup().activeGroupCount());
    ThreadGroup[] tg = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];
    Thread.currentThread().getThreadGroup().enumerate(tg);
    for (int i = 0; i < tg.length; i++)
        System.out.println("第一個執行緒組名稱為:" + tg[i].getName());
}

看一下執行結果:

A處執行緒:main, 所屬執行緒:main, 組中有執行緒組數量:0
B處執行緒:main, 所屬執行緒:main, 組中有執行緒組數量:1
第一個執行緒組名稱為:新的組

從結果看,例項化了一個group出來,沒有指定執行緒組,那麼自動歸到當前執行緒所屬的執行緒組中,也就是隱式地在一個執行緒組中新增了一個子執行緒組。

 

根執行緒組

看一下根執行緒組: 

public static void main(String[] args)
{
    System.out.println(Thread.currentThread().getThreadGroup().getParent().getName());
    System.out.println(Thread.currentThread().getThreadGroup().getParent().getParent().getName());
}

看一下執行結果:

system
Exception in thread "main" java.lang.NullPointerException
    at com.xrq.example.e49.TestMain49.main(TestMain49.java:11)

執行結果可以得出兩個結論:

1、根執行緒組就是系統執行緒組system

2、拋空指標異常是因為系統執行緒組上已經沒有執行緒組了,所以system的getParent()方法返回的是null,對null呼叫getName()方法自然是NullPointerException

關於根執行緒組,看一下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;
}

一個私有構造方法,說明不是對開發者開放的。註釋上已經寫得很清楚了,這是C程式碼呼叫的,用於構建系統執行緒組。

 

批量停止組內執行緒

使用執行緒組自然是要對執行緒做批量管理的,到目前為止我們似乎都沒有看見如何對執行緒組內的執行緒做批量操作,最後來看一下批量操作執行緒組內的執行緒:

public class MyThread50 extends Thread
{
    public MyThread50(ThreadGroup tg, String name)
    {
        super(tg, name);
    }
    
    public void run()
    {
        System.out.println("ThreadName = " + Thread.currentThread().getName() + 
                "準備開始死迴圈了");
        while (!this.isInterrupted()){}
        System.out.println("ThreadName = " + Thread.currentThread().getName() + 
                "結束了");
    }
}

開3個執行緒:

public static void main(String[] args) throws InterruptedException
{
    ThreadGroup tg = new ThreadGroup("我的執行緒組");
    MyThread50 mt = null;
    for (int i = 0; i < 3; i++)
    {
        mt = new MyThread50(tg, "執行緒" + i);
        mt.start();
    }
    Thread.sleep(5000);
    tg.interrupt();
    System.out.println("呼叫了interrupt()方法");
}

看一下執行結果:

ThreadName = 執行緒0準備開始死迴圈了
ThreadName = 執行緒2準備開始死迴圈了
ThreadName = 執行緒1準備開始死迴圈了
呼叫了interrupt()方法
ThreadName = 執行緒2結束了
ThreadName = 執行緒1結束了
ThreadName = 執行緒0結束了

看到呼叫了ThreadGroup中的interrupt()方法批量中斷了執行緒組內的執行緒,這就是ThreadGroup的作用。更多執行緒組的操作可以檢視JDK API。

相關文章