1. 程式人生 > >線程組的使用

線程組的使用

cat 線程數 活動 組織結構 array 進行 end 結束 這樣的

1.線程組的概念

  可以把線程歸屬到某一個線程組中,線程組中可以有線程對象,也可以有線程組,組中還可以用線程。這樣的組織結構有點類似於樹的形式。

技術分享圖片

  線程組的作用是,可以批量的管理線程或者線程組對象,有效地對線程或線程組對象進行組織。

2.線程對象關聯線程組:1級關聯

  1級關聯就是父對象中有子對象,但並不創建子孫對象。比如創建一些線程,為了有效地對這些線程進行組織管理,通常的做法就是創建一個線程組,然後將部分線程歸屬到該組中。這樣可以對零散的線程對象進行有效的組織與規劃。

package cn.qlq.thread.sixteeen;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class Demo1 implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(Demo1.class); @Override public void run() { LOGGER.info("threadname ->{},threadGroup ->{}", Thread.currentThread().getName(), Thread.currentThread().getThreadGroup().getName()); }
public static void main(String[] args) { ThreadGroup threadGroup = new ThreadGroup("g1"); new Thread(threadGroup, new Demo1()).start(); new Thread(threadGroup, new Demo1()).start(); } }

結果:

18:53:40 [cn.qlq.thread.sixteeen.Demo1]-[INFO] threadname ->Thread-1,threadGroup ->g1

18:53:40 [cn.qlq.thread.sixteeen.Demo1]-[INFO] threadname ->Thread-0,threadGroup ->g1

3.線程對象關聯線程組:多級關聯

  多級關聯就是父對象中有子對象,子對象中有子孫對象。不支持這種寫法,不利於管理。

例如:在main所在的線程組中增加一個subMain線程組,並在subMain線程組增加一個線程。

package cn.qlq.thread.sixteeen;

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

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

    public static void main(String[] args) {
        LOGGER.info("main線程組,名字為:{}", Thread.currentThread().getThreadGroup().getName());

        ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
        ThreadGroup threadGroup2 = new ThreadGroup(threadGroup, "subMain");
        new Thread(threadGroup2, new Runnable() {
            @Override
            public void run() {
                LOGGER.info("threadname ->{},threadGroup ->{}", Thread.currentThread().getName(),
                        Thread.currentThread().getThreadGroup().getName());
                try {
                    Thread.sleep(2 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        ThreadGroup[] threadGroups = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];
        Thread.currentThread().getThreadGroup().enumerate(threadGroups);
        LOGGER.info("main線程有{}線程組,名字為:{}", threadGroups.length, threadGroups[0].getName());

        Thread[] threads = new Thread[threadGroups[0].activeCount()];
        threadGroups[0].enumerate(threads);
        LOGGER.info("" + threads[0].getName());
    }
}

結果:(可以看出main線程所處的線程組名字為main)

19:16:36 [cn.qlq.thread.sixteeen.Demo2]-[INFO] main線程組,名字為:main
19:16:36 [cn.qlq.thread.sixteeen.Demo2]-[INFO] threadname ->Thread-0,threadGroup ->subMain
19:16:36 [cn.qlq.thread.sixteeen.Demo2]-[INFO] main線程有1線程組,名字為:subMain
19:16:36 [cn.qlq.thread.sixteeen.Demo2]-[INFO] Thread-0

註意:activeCount()和activeGroupCount()的數目不是固定的,是系統中環境的一個快照。方法enumerate的作用是復制線程或者線程組,可以理解為將樹形結構變為水平的數組結構。

4.線程自動歸屬

  如果沒有指定,默認創建的線程的線程組是當前線程所處的線程組;如果創建一個線程組沒有指定,線程組的父線程組默認是當前線程鎖處的線程組。

package cn.qlq.thread.sixteeen;

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

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

    public static void main(String[] args) {
        LOGGER.info("main線程組,名字為:{}", Thread.currentThread().getThreadGroup().getName());

        new Thread(new Runnable() {
            @Override
            public void run() {
                LOGGER.info("threadname ->{},threadGroup ->{}", Thread.currentThread().getName(),
                        Thread.currentThread().getThreadGroup().getName());
                try {
                    Thread.sleep(2 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        ThreadGroup threadGroup2 = new ThreadGroup("subMain");
        LOGGER.info("threadGroup2線程組的父線程組是->{}", threadGroup2.getParent().getName());
    }
}

結果:

19:20:53 [cn.qlq.thread.sixteeen.Demo3]-[INFO] main線程組,名字為:main
19:20:53 [cn.qlq.thread.sixteeen.Demo3]-[INFO] threadGroup2線程組的父線程組是->main
19:20:53 [cn.qlq.thread.sixteeen.Demo3]-[INFO] threadname ->Thread-0,threadGroup ->main

5.獲取根線程組

  根線程組是system,如果繼續獲取system.getParent()的話會報錯空指針異常。

package cn.qlq.thread.sixteeen;

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

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

    public static void main(String[] args) {
        LOGGER.info("main函數所在的線程組,名字為:{}", Thread.currentThread().getThreadGroup().getName());
        LOGGER.info("main線程組的父線程組名字為:{}", Thread.currentThread().getThreadGroup().getParent().getName());
        LOGGER.info("main線程組的父線程組的父線程組的名字為:{}",
                Thread.currentThread().getThreadGroup().getParent().getParent().getName());
    }
}

結果:

19:24:37 [cn.qlq.thread.sixteeen.Demo4]-[INFO] main函數所在的線程組,名字為:main
Exception in thread "main" 19:24:37 [cn.qlq.thread.sixteeen.Demo4]-[INFO] main線程組的父線程組名字為:system
java.lang.NullPointerException
at cn.qlq.thread.sixteeen.Demo4.main(Demo4.java:13)

6.線程組裏面加線程組

  用顯示的方式在當前main線程組中增加一個main2線程組。

package cn.qlq.thread.sixteeen;

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

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

    public static void main(String[] args) {
        LOGGER.info("線程組名字為:{},父線程組名稱:{}", Thread.currentThread().getThreadGroup().getName(),
                Thread.currentThread().getThreadGroup().getParent().getName());
        LOGGER.info("線程組中活動的線程數量為:{}", Thread.currentThread().getThreadGroup().activeCount());
        LOGGER.info("線程組中活動的線程組數量為:{}---加之前", Thread.currentThread().getThreadGroup().activeGroupCount());

        // 添加一個線程到當前線程組中
        ThreadGroup threadGroup = new ThreadGroup(Thread.currentThread().getThreadGroup(), "main2");
        LOGGER.info("線程組中活動的線程組數量為:{}---加之後", Thread.currentThread().getThreadGroup().activeGroupCount());

        // 將當前線程組復制到新創建的線程組數組中
        ThreadGroup[] threadGroups = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];
        Thread.currentThread().getThreadGroup().enumerate(threadGroups);
        for (ThreadGroup threadGroup2 : threadGroups) {
            LOGGER.info("" + threadGroup2.getName());
        }
    }
}

結果:

21:49:09 [cn.qlq.thread.sixteeen.Demo5]-[INFO] 線程組名字為:main,父線程組名稱:system
21:49:09 [cn.qlq.thread.sixteeen.Demo5]-[INFO] 線程組中活動的線程數量為:1
21:49:09 [cn.qlq.thread.sixteeen.Demo5]-[INFO] 線程組中活動的線程組數量為:0---加之前
21:49:09 [cn.qlq.thread.sixteeen.Demo5]-[INFO] 線程組中活動的線程組數量為:1---加之後
21:49:09 [cn.qlq.thread.sixteeen.Demo5]-[INFO] main2

7.批量停止線程組內的線程

  調用threadGroup.interrupt();會向組內正在運行的線程發出中斷信號。

package cn.qlq.thread.sixteeen;

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

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

    public static void main(String[] args) throws InterruptedException {

        ThreadGroup threadGroup = new ThreadGroup(Thread.currentThread().getThreadGroup(), "main2");
        LOGGER.info("線程組中活動的線程組數量為:{}---加之後", Thread.currentThread().getThreadGroup().activeGroupCount());
        for (int i = 0; i < 5; i++) {
            new Thread(threadGroup, new Runnable() {
                @Override
                public void run() {
                    LOGGER.info("threadName->{}準備死循環", Thread.currentThread().getName());
                    while (!Thread.currentThread().isInterrupted()) {

                    }
                    LOGGER.info("threadName->{}結束死循環", Thread.currentThread().getName());
                }
            }).start();
        }

        Thread.sleep(2 * 1000);
        // 向線程組發出終端信號
        threadGroup.interrupt();
    }
}

結果:

21:56:30 [cn.qlq.thread.sixteeen.Demo6]-[INFO] 線程組中活動的線程組數量為:1---加之後
21:56:30 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-0準備死循環
21:56:30 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-1準備死循環
21:56:30 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-2準備死循環
21:56:30 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-3準備死循環
21:56:30 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-4準備死循環
21:56:32 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-2結束死循環
21:56:32 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-1結束死循環
21:56:32 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-0結束死循環
21:56:32 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-3結束死循環
21:56:32 [cn.qlq.thread.sixteeen.Demo6]-[INFO] threadName->Thread-4結束死循環

看源碼發現threadGroup.interrupt();是向組內的線程發出終端信號:

    public final void interrupt() {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            checkAccess();
            for (int i = 0 ; i < nthreads ; i++) {
                threads[i].interrupt();
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        for (int i = 0 ; i < ngroupsSnapshot ; i++) {
            groupsSnapshot[i].interrupt();
        }
    }

8.遞歸與非遞歸獲得組內對象

8.1遞歸與非遞歸獲得組內的組

  向main組內增加main2組,main2組中中增加組main22,main22組中增加組main222
package cn.qlq.thread.sixteeen;

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

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

    public static void main(String[] args) throws InterruptedException {
        // 向main組內增加main2組,main2中增加main22,main22中增加main222
        ThreadGroup threadGroup2 = new ThreadGroup(Thread.currentThread().getThreadGroup(), "main2");
        ThreadGroup threadGroup22 = new ThreadGroup(threadGroup2, "main22");
        ThreadGroup threadGroup222 = new ThreadGroup(threadGroup22, "main222");

        LOGGER.info("==========={}=======", Thread.currentThread().getThreadGroup().activeGroupCount());

        // 分配空間,不一定全部用完
        ThreadGroup threadGroups1[] = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];
        ThreadGroup threadGroups2[] = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];

        // 傳入true是遞歸獲取其子孫組
        Thread.currentThread().getThreadGroup().enumerate(threadGroups1, true);
        for (ThreadGroup t : threadGroups1) {
            LOGGER.info("threadGroupName->{}", t.getName());
        }

        // 傳入false是只獲取直屬的線程組
        LOGGER.info("==========={}=======", Thread.currentThread().getThreadGroup().activeGroupCount());
        Thread.currentThread().getThreadGroup().enumerate(threadGroups2, false);
        for (int i = 0; i < threadGroups2.length; i++) {
            if (threadGroups2[i] != null) {
                LOGGER.info("threadGroupName->{}", threadGroups2[i].getName());
            } else {
                LOGGER.info("{}為null", i);
            }
        }

    }
}

結果:

22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] ===========3=======
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] threadGroupName->main2
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] threadGroupName->main22
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] threadGroupName->main222
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] ===========3=======
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] threadGroupName->main2
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] 1為null
22:09:13 [cn.qlq.thread.sixteeen.Demo7]-[INFO] 2為null

8.2 遞歸與非遞歸獲取組內的線程

  向main組內增加一個線程,增加一個組main2,並且向main2組中增加1個線程(相當於main組的子孫線程)。

package cn.qlq.thread.sixteeen;

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

public class Demo8 extends Thread {

    @Override
    public void run() {
        while (true) {

        }
    }

    private static final Logger LOGGER = LoggerFactory.getLogger(Demo8.class);

    public static void main(String[] args) throws InterruptedException {
        Thread t0 = new Thread(new Demo8());
        t0.start();

        // 向main組內增加main2組和一個線程對象
        ThreadGroup threadGroup2 = new ThreadGroup(Thread.currentThread().getThreadGroup(), "main2");
        Thread t1 = new Thread(threadGroup2, new Demo8());
        t1.start();

        LOGGER.info("==========={}=======", Thread.currentThread().getThreadGroup().activeCount());

        // 分配空間,不一定全部用完
        Thread thread1[] = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
        Thread thread2[] = new Thread[Thread.currentThread().getThreadGroup().activeCount()];

        // 傳入true是遞歸獲取其子孫線程
        Thread.currentThread().getThreadGroup().enumerate(thread1, true);
        for (Thread t : thread1) {
            LOGGER.info("threadName->{}", t.getName());
        }

        // 傳入false是只獲取直屬的線程
        LOGGER.info("==========={}=======", Thread.currentThread().getThreadGroup().activeCount());
        Thread.currentThread().getThreadGroup().enumerate(thread2, false);
        for (int i = 0; i < thread2.length; i++) {
            if (thread2[i] != null) {
                LOGGER.info("threadName->{}", thread2[i].getName());
            } else {
                LOGGER.info("{}為null", i);
            }
        }

    }
}

結果:

22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] ===========3=======
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] threadName->main
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] threadName->Thread-1
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] threadName->Thread-3
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] ===========3=======
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] threadName->main
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] threadName->Thread-1
22:24:32 [cn.qlq.thread.sixteeen.Demo8]-[INFO] 2為null

總結:

  1. threadGroup.activeGroupCount()是返回線程組的子孫線程組,包括非直屬的線程組。

  2. threadGroup.enumerate(ThreadGroup list[], boolean recurse)是將threadGroup組內的組復制到list數組中,第二個參數是是否遞歸(也就是是否包含子孫),默認的是true。也就是此方法可以將一個樹形的組結構變為一個水平的數組結構。源碼如下:

    public int enumerate(ThreadGroup list[]) {
        checkAccess();
        return enumerate(list, 0, true);
    }
    public int enumerate(ThreadGroup list[], boolean recurse) {
        checkAccess();
        return enumerate(list, 0, recurse);
    }
    private int enumerate(ThreadGroup list[], int n, boolean recurse) {
        int ngroupsSnapshot = 0;
        ThreadGroup[] groupsSnapshot = null;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            int ng = ngroups;
            if (ng > list.length - n) {
                ng = list.length - n;
            }
            if (ng > 0) {
                System.arraycopy(groups, 0, list, n, ng);
                n += ng;
            }
            if (recurse) {
                ngroupsSnapshot = ngroups;
                if (groups != null) {
                    groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
                } else {
                    groupsSnapshot = null;
                }
            }
        }
        if (recurse) {
            for (int i = 0 ; i < ngroupsSnapshot ; i++) {
                n = groupsSnapshot[i].enumerate(list, n, true);
            }
        }
        return n;
    }

 

  3. threadGroup.activeCount()是返回線程組的子孫線程,包括非直屬的線程。

  4. threadGroup.enumerate(Thread list[], boolean recurse)是將threadGroup組內的線程復制到list數組中,第二個參數是是否遞歸(也就是是否包含子孫),默認的是true。也就是此方法可以將一個樹形的線程結構變為一個水平的數組結構。源碼如下:

    public int enumerate(Thread list[]) {
        checkAccess();
        return enumerate(list, 0, true);
    }
    public int enumerate(Thread list[], boolean recurse) {
        checkAccess();
        return enumerate(list, 0, recurse);
    }

    private int enumerate(Thread list[], int n, boolean recurse) {
        int ngroupsSnapshot = 0;
        ThreadGroup[] groupsSnapshot = null;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            int nt = nthreads;
            if (nt > list.length - n) {
                nt = list.length - n;
            }
            for (int i = 0; i < nt; i++) {
                if (threads[i].isAlive()) {
                    list[n++] = threads[i];
                }
            }
            if (recurse) {
                ngroupsSnapshot = ngroups;
                if (groups != null) {
                    groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
                } else {
                    groupsSnapshot = null;
                }
            }
        }
        if (recurse) {
            for (int i = 0 ; i < ngroupsSnapshot ; i++) {
                n = groupsSnapshot[i].enumerate(list, n, true);
            }
        }
        return n;
    }

  

線程組的使用