1.循环屏障的使用
如果打一场游戏,必须等待游戏的玩家足够以后才开始,并且为了公平,所有玩家必须同时进入游戏。循环屏障CyclickBarrier
就是为了解决这种场景而设计的
假如现在游戏需要10人才能开始,并且所有玩家需要同时进入游戏。我们可以这样实现
public static void main(String[] args) { CyclicBarrier barrier = new CyclicBarrier(10, () -> { System.out.println("Ready to start,please prepare..."); }); for (int i = 0; i < 10; i++) { final int finalI = i; new Thread(()->{ try { Thread.sleep((long) (2000 * new Random().nextDouble())); System.out.println("Player:" + finalI + " prepared,"+ barrier.getNumberWaiting()+ "/10"); barrier.await(); System.out.println("Player:" + finalI + " Join Game..."); } catch (InterruptedException | BrokenBarrierException exception) { exception.printStackTrace(); } }).start(); } }
输出结果如下。
可以看到循环屏障会不断的阻挡线程,知道线程数量足够多时,再一起冲破线程屏障。并且在冲破屏障后,可以执行屏障创建时指定的任务。
屏障是可以循环使用的,在被冲破后,会重新开始计数,继续阻挡后续的线程。比如我们将屏障的初始容量设置为5,看看执行结果。
public static void main(String[] args) { CyclicBarrier barrier = new CyclicBarrier(5, () -> { System.out.println("Ready to start,please prepare..."); }); for (int i = 0; i < 10; i++) { final int finalI = i; new Thread(()->{ try { Thread.sleep((long) (2000 * new Random().nextDouble())); System.out.println("Player:" + finalI + " prepared,"+ barrier.getNumberWaiting()+ "/5"); barrier.await(); System.out.println("Player:" + finalI + " Join Game..."); } catch (InterruptedException | BrokenBarrierException exception) { exception.printStackTrace(); } }).start(); } }
执行结果如下。
当然,除了自动清零,我们也可以将循环屏障手动置零
public static void main(String[] args) throws InterruptedException { CyclicBarrier barrier = new CyclicBarrier(5, () -> { System.out.println("Ready to start,please prepare..."); }); for (int i = 0; i < 3; i++) { new Thread(()->{ try { barrier.await(); } catch (InterruptedException | BrokenBarrierException exception) { exception.printStackTrace(); } }).start(); } Thread.sleep(500); System.out.println(barrier.getNumberWaiting()); barrier.reset(); System.out.println(barrier.getNumberWaiting()); }
执行结果如下。报了BrokenBarrierException
,这是因为在循环屏障数达到3以后,还没有冲破屏障,我们就将循环屏障的计数清零了,之前处于等待状态的线程全部被中断,屏障被破坏了。
要是处于等待状态的线程被中断了呢?循环屏障的计数会不会自动减少
public static void main(String[] args) { CyclicBarrier barrier = new CyclicBarrier(5, () -> { System.out.println("Ready to start,please prepare..."); }); Runnable r = ()-> { try { barrier.await(); } catch (InterruptedException | BrokenBarrierException exception) { exception.printStackTrace(); } }; Thread t = new Thread(r); t.start(); t.interrupt(); new Thread(r).start(); }
其结果如下。
第一个异常那个信息很好理解,是异常中断。第二个异常信息是因为屏障里的线程被取消,导致本轮屏障被破坏了。可以这么理解,约了三个朋友一起打麻将,结果有一个坑爹的队友临时爽约了,那他一个人就导致这局麻将组不成了。当然,我们还可以重新组局,我们也可以使用reset
对屏障重新计数。
public static void main(String[] args) throws InterruptedException { CyclicBarrier barrier = new CyclicBarrier(5, () -> { System.out.println("Ready to start,please prepare..."); }); Runnable r = ()-> { try { barrier.await(); } catch (InterruptedException | BrokenBarrierException exception) { exception.printStackTrace(); } }; Thread t = new Thread(r); t.start(); t.interrupt(); Thread.sleep(500); // 等待中断结束 barrier.reset(); new Thread(r).start(); }
执行结果如下。
大家是不是有种感觉,CountdownLatch
和CyclickBarrier
还挺相似的。我们来总结下他们的区别。