zoukankan      html  css  js  c++  java
  • 线程组的使用

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

      

  • 相关阅读:
    Android中设置APP应用字体不缩放,文字不随系统字体大小变化
    day02 作业
    day01
    2018.11.2
    2018.11.1
    2018.10.25
    2018.10.24
    2018.10.23
    2018.10.20
    2018.10.19学习总结
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/10210606.html
Copyright © 2011-2022 走看看