1. CyclicBarrier 是什么?
CyclicBarrier的字面意思是可循环(Cyclic)使用的屏障(Barrier),它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrier的await()方法。
白话讲CountDownLatch是减法倒计时,CyclicBarrier是加法,比如人员开会,一共有7个人,只有7个人都到了才能开会。
该类用于协调多个线程同步执行操作的场合。
2. 怎么使用 CyclicBarrier
2.1 构造方法
public CyclicBarrier(int parties)
public CyclicBarrier(int parties, Runnable barrierAction)
解析:
- parties 是参与线程的个数
- 第二个构造方法有一个 Runnable 参数,当屏障被触发时执行的命令。这个Runnable任务在 CyclicBarrier的数目到达后,所有线程被唤醒前执行。
2.2 重要方法
public int await() throws InterruptedException, BrokenBarrierException
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException
解析:
- 线程调用 await() 表示自己已经到达了屏障,然后当前线程被阻塞。
- BrokenBarrierException 表示栅栏已经被破坏,破坏的原因可能是其中一个线程 await() 时被中断或者超时
2.3 基本使用
使用场景:10个工程师一起来公司应聘,招聘方式分为笔试和面试。首先,要等人到齐后,开始笔试;笔试结束之后,再一起参加面试。把10个人看作10个线程,10个线程之间的同步过程如下图所示:
代码实现
public class MyThread extends Thread {
private CyclicBarrier cyclicBarrier;
private Random random=new Random();
@Override
public void run() {
try {
Thread.sleep(random.nextInt(2000));
System.out.println("线程"+Thread.currentThread().getName()+"已经到达公司");
cyclicBarrier.await();
Thread.sleep(random.nextInt(2000));
System.out.println("线程"+Thread.currentThread().getName()+"已经开始笔试");
cyclicBarrier.await();
Thread.sleep(random.nextInt(2000));
System.out.println("线程"+Thread.currentThread().getName()+"已经面试结束");
} catch (InterruptedException e) {
e.printStackTrace();
}catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
public MyThread(String name,CyclicBarrier cyclicBarrier){
super(name);
this.cyclicBarrier=cyclicBarrier;
}
}
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"到达,一起开始-------");
}
});
for (int i = 0; i <5 ; i++) {
MyThread myThread = new MyThread("线程"+i,cyclicBarrier);
myThread.start();
}
}
打印结果:
线程线程3已经到达公司
线程线程1已经到达公司
线程线程4已经到达公司
线程线程0已经到达公司
线程线程2已经到达公司
线程2到达,一起开始-------
线程线程4已经开始笔试
线程线程2已经开始笔试
线程线程3已经开始笔试
线程线程0已经开始笔试
线程线程1已经开始笔试
线程1到达,一起开始-------
线程线程1已经面试结束
线程线程0已经面试结束
线程线程2已经面试结束
线程线程4已经面试结束
线程线程3已经面试结束
3. CyclicBarrier 使用场景
可以用于多线程计算数据,最后合并计算结果的场景。
public class TestCyc2 {
public static void main(String[] args) {
final ConcurrentHashMap<String,Integer> map=new ConcurrentHashMap<String,Integer>();
FutureTask futerTask=new FutureTask<Integer>(new Callable<Integer>() {
public Integer call() throws Exception {
Integer count=0;
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for (Map.Entry<String,Integer> e :entries){
String key = e.getKey();
int value = e.getValue();
System.out.println("key:"+key+","+"value:"+value);
count+=value;
}
System.out.println("call方法 count"+count);
return count;
}
});
final CyclicBarrier barrier=new CyclicBarrier(5,futerTask );
for(int i=0;i<5;i++){
final int k=i;
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(3000);
map.put(Thread.currentThread().getName(),k);
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
try {
Integer integer = (Integer) futerTask.get();
System.out.println("最后结果="+integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
打印结果: