您的位置:首页 > 编程语言 > Java开发

Java 并发:CyclicBarrier 实现线程协作控制

2019-04-20 11:02 281 查看
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wojiushiwo945you/article/details/89414422

背景

电脑桌面上有一张“Java 并发编程知识思维导图”,这两天刚好又用到了 CyclicBarrier 类,趁着周末整理下 CyclicBarrier 的用法。

Java 并发知识思维导图


(此图来源于网络,原文链接不详,如有侵权,请私信联系本人删除)

CyclicBarrier 位于图中协作类的范畴,JKD 官方文档中对它的解释是这样的:

synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other.

适用于一组线程需要等待其他所有线程都到达某个点的场景。

Barrier ,是障碍物、栅栏之意,线程一旦调用它的 await 方法后就处于被阻拦状态,直到所有线程都执行过 await 后,线程的阻塞状态才解除。Cyclic ,是循环的意思,是指当所有线程都解除阻塞状态后,它还可以被重复使用。

基本用法

CyclicBarrier 的构造方法包含两个参数:

public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}

parties:等待在栅栏上的线程总数;
barrierAction:当所有线程都达到栅栏后需要执行的动作,JVM 会开启一个新线程执行该任务。

实践编码

模拟一种集体出游的行动过程,每个参与者都先按自己的情况安排事情,然后到集合点集合;等待所有参与者都达到集合地点乘坐旅游大巴到达出游地,下车后再自由行动。这里就涉及的等待其他人都达到的场景,就可以用 CyclicBarrier 实现。

这里定义一个游客类 VisitorTask:

import java.util.concurrent.CyclicBarrier;

public class VisitorTask implements Runnable{

private CyclicBarrier barrier;

private boolean isSleep;

private VisitorTask(CyclicBarrier barrier,boolean isSleep) {
this.barrier = barrier;
this.isSleep = isSleep;
}

@Override
public void run() {
//先执行自己的事情
System.out.println(Thread.currentThread().getName()+"进行出游准备。");
System.out.println(Thread.currentThread().getName()+"到达集合点。");

if(isSleep) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

//等待其他线程都到达该栅栏
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}

//再执行其他事情
System.out.println(Thread.currentThread().getName()+"自由观光。");
}
}

接着编写控制类,启动 N 个任务,并开启一个栅栏对象:

public static void main(String[] args) {
int count= 5;
final ScheduledExecutorService service = Executors.newScheduledThreadPool(count);

// 线程协作类:等待所有任务都执行完成后,主线程则执行收尾工作
CyclicBarrier barrier = new CyclicBarrier(count, new Runnable() {
@Override
public void run() {
// 关闭线程池
service.shutdown();

// 统计操作总耗时
System.out.println(Thread.currentThread().getName() + " all visitor are here");
}
});

//开启N个线程,偶数线程执行 await之前休眠
for(int i=0;i<count;i++) {
service.submit(new VisitorTask(barrier,i%2==0));
}
}

运行结果:

pool-1-thread-1进行出游准备。
pool-1-thread-4进行出游准备。
pool-1-thread-3进行出游准备。
pool-1-thread-3到达集合点。
pool-1-thread-2进行出游准备。
pool-1-thread-4到达集合点。
pool-1-thread-1到达集合点。
pool-1-thread-2到达集合点。
pool-1-thread-5进行出游准备。
pool-1-thread-5到达集合点。
pool-1-thread-5 all visitor are here
pool-1-thread-5自由观光。
pool-1-thread-4自由观光。
pool-1-thread-1自由观光。
pool-1-thread-3自由观光。
pool-1-thread-2自由观光。

从运行结果来看:在所有线程都调用 await 之前,已经达到的线程都是被阻拦状态的,当所有线程都调用过 await 后 barrier.await() == 0 为真,JVM 开启一个新线程执行 Barrier 的回调任务,然后所有线程继续 await 后面的操作。

编程启示录

再总结下前文的思维导图:

  1. 分工:高效的拆解任务分给线程
  2. 协作:线程之间的协作控制
  3. 互斥:保证同一时刻只允许一个线程访问共享资源

这是 Java 并发编程的主要内容,并发包里面的工具类的用法并不复杂,尝试着多用用,并发编程其实还是孰能生巧的事情!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐