CyclicBarrier使用场景
用于协调多个线程同步执行操作的场合,所有线程等待完成,然后一起做事情( 相互之间都准备好,然后一起做事情 )
例如百米赛跑,必须等待所有运动员都准备好了,才能比赛。
运动员准备好,裁判也准备好,才能开始正式比赛。
例子
CyclicBarrier cyclicBarrier = new CyclicBarrier(3); new Thread(() -> { try { Thread.sleep(1000); System.out.println("t1 在准备 "); cyclicBarrier.await(); // 等另外一个线程准备好 然后开始做事情 System.out.println("t1 准备好了 "); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } }).start(); new Thread(() -> { try { Thread.sleep(2000); System.out.println("t2 在准备 "); cyclicBarrier.await(); // 等另外一个线程准备好 然后开始做事情 System.out.println("t2 准备好了 "); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } }).start(); System.out.println(" 裁判 在准备 "); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println(" 裁判 准备好了 "); while (true){ if (cyclicBarrier.getNumberWaiting()==0){ System.out.println(" 时间到: 开始比赛"); return; } }
关于CyclicBarrier的底层执行流程总结:
- 1、初始化CyclicBarrier中的各种成员变量,包括parties、count以及Runnable(可选);
- 2、当调用await()方法时,底层会先检查计数器是否已经归零,如果是的话,那么就首先执行可选的Runnable,接下来开始下一个generation;(注意:这里只是调用Runnable的run()方法,并不是调用start()方法开启另一个线程)
- 3、在下一个分代中,将会重置count值为parties,并且创建新的Generation实例;
- 4、同时会调用Condition的singalAll方法,唤醒所有在屏障前面等待的线程,让其开始继续执行;(注意:当有可选的Runnable时,是执行完run()方法中的汇总操作,其他线程才会继续执行)
- 5、如果计数器没有归零,那么当前的调用线程将会通过Condition的await方法,在屏障前进行等待;
- 6、以上所有执行流程均在lock锁的控制范围内,不会出现并发情况。
- 7、在下一个分代时,该屏障又可以继续使用,例如计数器是3,线程1,线程2和线程3冲破了当前屏障后,下一个分代的屏障可以去给线程4,线程5和线程6使用,也可以又给线程1,线程2和线程3使用(自己总结的)
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() { @Override public void run() { System.out.println("汇总 然后继续执行没有执行的部分"); } });
CountDownLatch是基于AQS实现的;而CyclicBarrier是基于ReentrantLock重入锁实现的,当然ReentrantLock也是基于AQS实现的,非要说CyclicBarrier也是基于AQS实现的也不为过。
await()方法有两层含义:
1、先检查前面是否已经有count个线程了,如果没有线程则会进入等待状态
2、当检测到屏障已经有count个线程了,则所有线程会冲出屏障继续执行(如果有Runnable参数的构造方法先执行汇总方法)