CyclicBarrier类简介

CyclicBarrier作用

CyclicBarrier是java.util.concurrent包下的一个类,是一个可以循环使用(Cylcic)的屏障(Barrier)。作用就是让一组线程到达一个屏障(同步点)时被阻塞,直到这组线程中的最后一个到达屏障时,屏障才会打开,之前阻塞的线程继续运行。过程如下图所示:
image.png
上图中的三个线程中各有一个barrier.await,任何一个线程在运行到barrier.await时都会进入阻塞等待状态,直到三个线程都到了barrier.await时才从await返回,三个线程同时继续地向后运行。通过它可以实现让一组线程等待至某个状态之后再全部同时执行,叫做“循环”是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。

主要方法

image.png
image.png
image.png
image.png
image.png

CyclicBarrier的使用

效果演示

  1. @Slf4j(topic = "c.CyclicBarrierTest")
  2. public class CyclicBarrierTest {
  3. public static void main(String[] args) {
  4. // 个数为2时才会继续执行
  5. CyclicBarrier cb = new CyclicBarrier(2, () -> {
  6. log.debug("兄弟们,准备好出发了吗");
  7. });
  8. new Thread(() -> {
  9. log.debug("线程1开始.." + new Date());
  10. try {
  11. cb.await(); // 当个数不足时,等待
  12. } catch (InterruptedException | BrokenBarrierException e) {
  13. e.printStackTrace();
  14. }
  15. log.debug("线程1继续向下运行..." + new Date());
  16. }).start();
  17. new Thread(() -> {
  18. log.debug("线程2开始.." + new Date());
  19. try {
  20. Thread.sleep(2000);
  21. } catch (InterruptedException ignored) {
  22. }
  23. try {
  24. cb.await(); // 2 秒后,线程个数够2,继续运行
  25. } catch (InterruptedException | BrokenBarrierException e) {
  26. e.printStackTrace();
  27. }
  28. log.debug("线程2继续向下运行..." + new Date());
  29. }).start();
  30. }
  31. }

运行结果:
image.png可以看到,在每个线程都调用await方法前,全部线程都被阻塞了,只要都调用到了await方法,全部线程才会一起继续执行。

CyclicBarrier使用时的注意事项

  • 如果在线程池中使用,一定要确保线程池的线程数量大于等于CyclicBarrier对象的规定阻塞线程数量,否则可能会导致到达栅栏的线程不足而无法“放行”的情况
  • 调用await()方法的次数一定要等于屏障中设置的阻塞线程的数量,否则会死锁

    CyclicBarrier和CountDownLatch的区别

  • 二者都能让一个或多个线程阻塞等待,都可以用在多个线程间的协调,起到线程同步的作用。但CountDownLatch是多个线程都进行了countDown之后才会触发时间,唤醒await在latch上的线程,执行完countDown操作之后会继续自己线程的工作。而CyclicBarrier是一个栅栏,用于同步所有调用await方法的线程,等到所有的线程都执行了await方法后,所有的线程才会返回各自执行自己的工作。

  • CountDownLatch计数器只能使用一次,而CyclicBarrier的计数器可以调用 reset() 方法重置,能处理更加复杂的业务场景。