Java多线程:线程组

Posted Java程序员老张

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程:线程组相关的知识,希望对你有一定的参考价值。

线程组

可以把线程归属到某一个线程组中,线程组中可以有线程对象,也可以有线程组,组中还可以有线程,这样的组织结构有点类似于树的形式,如图所示:

线程组的作用是:可以批量管理线程或线程组对象,有效地对线程或线程组对象进行组织。

线程关联线程组: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。

java多线程-CyclicBarrier

  • 介绍

  一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。 

  CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。 

  • 主要方法
  • 1 //设置parties、count及barrierCommand属性。   
    2 CyclicBarrier(int):   
    3 //当await的数量到达了设定的数量后,首先执行该Runnable对象。   
    4 CyclicBarrier(int,Runnable):   
    5 //通知barrier已完成线程   
    6 await():  
  • 应用场景

  1:CyclicBarrier 表示大家彼此等待,大家集合好后才开始出发,分散活动后又在i指定地点集合碰面,这就好比整个公司的人员利用周末时间集体郊游一样,先各自从家出发到公司集合后,再同时出发到公园游玩,在指定地点集合后再同时开始就餐。

  代码:

 1 public class CyclicBarrierTest {
 2     public static void main(String[] args){
 3         ExecutorService pool = Executors.newCachedThreadPool();
 4         final CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
 5         
 6         for (int i = 0; i < 3; i++) {
 7             Runnable runnable = new Runnable() {
 8                 @Override
 9                 public void run(){
10                     try {
11                         Thread.sleep(new Random().nextInt(5000));
12                     } catch (InterruptedException e) {
13                         // TODO Auto-generated catch block
14                         e.printStackTrace();
15                     }
16                     System.out.println(Thread.currentThread().getName()+"到达地点一,当前等待人数为"+(cyclicBarrier.getNumberWaiting()+1)+(cyclicBarrier.getNumberWaiting()+1==3?"继续出发":"继续等待"));
17                     try {
18                         cyclicBarrier.await();//障碍等待点
19                     } catch (InterruptedException e) {
20                         // TODO Auto-generated catch block
21                         e.printStackTrace();
22                     } catch (BrokenBarrierException e) {
23                         // TODO Auto-generated catch block
24                         e.printStackTrace();
25                     }
26                     try {
27                         Thread.sleep(new Random().nextInt(5000));
28                     } catch (InterruptedException e) {
29                         // TODO Auto-generated catch block
30                         e.printStackTrace();
31                     }
32                     System.out.println(Thread.currentThread().getName()+"到达地点二,当前等待人数为"+(cyclicBarrier.getNumberWaiting()+1)+(cyclicBarrier.getNumberWaiting()+1==3?"继续出发":"继续等待"));
33                     try {
34                         cyclicBarrier.await();//障碍等待点
35                     } catch (InterruptedException e) {
36                         // TODO Auto-generated catch block
37                         e.printStackTrace();
38                     } catch (BrokenBarrierException e) {
39                         // TODO Auto-generated catch block
40                         e.printStackTrace();
41                     }
42                     try {
43                         Thread.sleep(new Random().nextInt(5000));
44                     } catch (InterruptedException e) {
45                         // TODO Auto-generated catch block
46                         e.printStackTrace();
47                     }
48                     System.out.println(Thread.currentThread().getName()+"到达地点三,当前等待人数为"+(cyclicBarrier.getNumberWaiting()+1)+(cyclicBarrier.getNumberWaiting()+1==3?"人齐了出发":"继续等待"));
49                     try {
50                         cyclicBarrier.await();//障碍等待点
51                     } catch (InterruptedException e) {
52                         // TODO Auto-generated catch block
53                         e.printStackTrace();
54                     } catch (BrokenBarrierException e) {
55                         // TODO Auto-generated catch block
56                         e.printStackTrace();
57                     }
58                 }
59             };
60             pool.execute(runnable);
61         }
62         pool.shutdown();
63     }
64 }

  执行结果:

技术分享
1 pool-1-thread-3到达地点一,当前等待人数为1继续等待
2 pool-1-thread-1到达地点一,当前等待人数为2继续等待
3 pool-1-thread-2到达地点一,当前等待人数为3继续出发
4 pool-1-thread-1到达地点二,当前等待人数为1继续等待
5 pool-1-thread-3到达地点二,当前等待人数为2继续等待
6 pool-1-thread-2到达地点二,当前等待人数为3继续出发
7 pool-1-thread-3到达地点三,当前等待人数为1继续等待
8 pool-1-thread-2到达地点三,当前等待人数为2继续等待
9 pool-1-thread-1到达地点三,当前等待人数为3人齐了出发
View Code

  2:在某种需求中,比如一个大型的任务,常常需要分配好多子任务去执行,只有当所有子任务都执行完成时候,才能执行主任务,这时候,就可以选择CyclicBarrier了。

  代码:

 1 public class CyclicBarrierTest1 {
 2     public static void main(String[] args) {
 3         ExecutorService threadPool = Executors.newCachedThreadPool();
 4         CyclicBarrier barrier = new CyclicBarrier(5, new mainTask());
 5         for (int i = 0; i < 5; i++) {
 6             subTask subTask = new subTask(barrier);
 7             threadPool.execute(subTask);
 8         }
 9         threadPool.shutdown();
10     }
11 }
12 
13 class subTask implements Runnable{
14     private CyclicBarrier barrier;
15     
16     public subTask(CyclicBarrier barrier) {
17         super();
18         this.barrier = barrier;
19     }
20     @Override
21     public void run() {
22         System.out.println(Thread.currentThread().getName()+"正在执行");
23         try {
24             Thread.sleep(5000);
25         } catch (InterruptedException e) {
26             // TODO Auto-generated catch block
27             e.printStackTrace();
28         }
29         System.out.println(Thread.currentThread().getName()+"执行完毕,等待其他结果");
30         try {
31             barrier.await();
32         } catch (InterruptedException e) {
33             // TODO Auto-generated catch block
34             e.printStackTrace();
35         } catch (BrokenBarrierException e) {
36             // TODO Auto-generated catch block
37             e.printStackTrace();
38         }
39         
40     }
41 }
42 class mainTask implements Runnable{
43 
44     @Override
45     public void run() {
46         System.out.println("总任务执行完毕");
47     }
48     
49 }

  执行结果:

技术分享
 1 pool-1-thread-2正在执行
 2 pool-1-thread-3正在执行
 3 pool-1-thread-1正在执行
 4 pool-1-thread-4正在执行
 5 pool-1-thread-5正在执行
 6 pool-1-thread-2执行完毕,等待其他结果
 7 pool-1-thread-5执行完毕,等待其他结果
 8 pool-1-thread-1执行完毕,等待其他结果
 9 pool-1-thread-4执行完毕,等待其他结果
10 pool-1-thread-3执行完毕,等待其他结果
11 总任务执行完毕
View Code

  另外,CyclicBarrier是可以重用的,它可以在使用完后继续使用,这就是Cyclic(循环)的意思。

 

以上是关于Java多线程:线程组的主要内容,如果未能解决你的问题,请参考以下文章

神仙文档啊!JVM,多线程,MySQL,分布式应有尽有!

Java学习之二(线程(了解) JVM GC 垃圾回收)

清华师哥 每周 花6 小时带你学 Java:JVM高并发多线程算法微服务等。薪资咔咔咔往上涨!

Java面试题库,Java下载安装教程环境变量

kafka应用难点,一文轻松搞定

java培训周期一般多长,已拿意向书!