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人齐了出发
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 总任务执行完毕
另外,CyclicBarrier是可以重用的,它可以在使用完后继续使用,这就是Cyclic(循环)的意思。
以上是关于Java多线程:线程组的主要内容,如果未能解决你的问题,请参考以下文章