CyclicBarrier和CountDownLatch一样是一种同步辅助工具
CyclicBarrier 它允许一组线程在一个共同的屏障点彼此等待,所有线程到达屏障点后再全部同时执行。固定数量的线程在程序中必须彼此等待的时候,CyclicBarrier非常有用
同CountDownLatch不同的是:
- CyclicBarrier的某个线程运行到某个点上之后,该线程即停止运行,直到所有的线程都到达了这个点,所有线程才重新运行;CountDownLatch则不是,某线程运行到某个点上之后,只是给某个数值-1而已,该线程继续运行
- CyclicBarrier只能唤起一个任务,CountDownLatch可以唤起多个任务
- CyclicBarrier可重用,CountDownLatch不可重用,计数值为0该CountDownLatch就不可再用了
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CyclicBarrier;
public class Person implements Runnable {
private CyclicBarrier barrier;
private String name;
public Person(CyclicBarrier barrier, String name) {
this.barrier = barrier;
this.name = name;
}
@Override
public void run() {
try {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
System.out.println(sdf.format(new Date()) + " " + name + "出发去饭店");
System.out.println(sdf.format(new Date()) + " " + name + "到了饭店");
barrier.await();
System.out.println(sdf.format(new Date()) + " " + name + "开始吃饭");
System.out.println(sdf.format(new Date()) + " " + name + "吃完了");
// 重用CyclicBarrier
barrier.await();
System.out.println(sdf.format(new Date()) + " " + name + "离开餐厅");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
CyclicBarrier barrier = new CyclicBarrier(3);
List<Thread> threads = new ArrayList<>(3);
threads.add(new Thread(new Person(barrier, "张三")));
threads.add(new Thread(new Person(barrier, "李四")));
threads.add(new Thread(new Person(barrier, "王五")));
for (Thread thread : threads) {
thread.start();
}
//等待所有线程跑完
for (Thread thread : threads) {
thread.join();
}
}
经过测试:当使用 CyclicBarrier 和 不使用 CyclicBarrier
使用 CyclicBarrier:
17:31:42.395 张三出发去饭店
17:31:42.395 李四出发去饭店
17:31:42.396 张三到了饭店
17:31:42.395 王五出发去饭店
17:31:42.396 李四到了饭店
17:31:42.396 王五到了饭店
17:31:42.397 王五开始吃饭
17:31:42.397 张三开始吃饭
17:31:42.397 李四开始吃饭
17:31:42.397 张三吃完了
17:31:42.397 王五吃完了
17:31:42.397 李四吃完了
17:31:42.397 张三离开餐厅
17:31:42.397 李四离开餐厅
17:31:42.397 王五离开餐厅
不使用 CyclicBarrier:
17:27:36.764 张三出发去饭店
17:27:36.767 张三到了饭店
17:27:36.768 张三开始吃饭
17:27:36.768 张三吃完了
17:27:36.768 张三离开餐厅
17:27:36.764 王五出发去饭店
17:27:36.764 李四出发去饭店
17:27:36.769 李四到了饭店
17:27:36.769 李四开始吃饭
17:27:36.769 李四吃完了
17:27:36.769 李四离开餐厅
17:27:36.769 王五到了饭店
17:27:36.770 王五开始吃饭
17:27:36.770 王五吃完了
17:27:36.770 王五离开餐厅
原理:
- 在CyclicBarrier的内部定义了一个ReentrantLock的对象,然后再利用这个ReentrantLock对象生成一个Condition的对象
- 每当一个线程调用CyclicBarrier的await方法时,首先把剩余屏障的线程数减1
- 然后判断剩余屏障数是否为0:如果不是,利用Condition的await方法阻塞当前线
- 如果是,首先利用Condition的signalAll方法唤醒所有线程
- 最后重新生成Generation对象以实现屏障的循环使用
参考:
https://zhuanlan.zhihu.com/p/140324377?from_voters_page=true