线程组的使用

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;
    }

 

 

  

posted @ 2019-01-02 22:30  QiaoZhi  阅读(1519)  评论(0编辑  收藏  举报