本文的概念来自《Java并发编程实战》
我们前面已经介绍了通过闭锁来启动一组相关的操作,或者等待一组相关的操作结束。闭锁是一次性对象,一旦进入终止状态就不能被重置。
栅栏(Barrier)类似于闭锁,它能阻塞一组线程直到某个事件发生。栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用户等待其他线程。栅栏用于实现一些协议,例如几个家庭决定在某个地方集合:“所有人6:00在麦当劳碰头,到了以后要等其他人,之后在讨论下一步要做的事情。”
CyclicBarrier可以使一定数量的参与方反复的在栅栏位置汇集,它在并行迭代算法中非常常用:这种算法通常将一个问题拆分成为一系列相互独立的自问题。当线程到达栅栏位置时将调用await()方法,这个方法将阻塞直到所有的线程到达栅栏位置。如果所有的线程都到达栅栏位置,那么栅栏将打开,此时所有的线程都被释放,而栅栏将被重置一遍下次使用。如果对await()方法的调用超时,或者await阻塞的线程被中断,那么栅栏就认为是被打破了,所有阻塞的await都将终止并且抛出BorkenBarrierException。如果成功的通过栅栏,那么await将会为每个线程都返回一个唯一的索引号,我们 可以利用这些索引来”选举”产生一个“领导线程”,并在下一次迭代中由该领导线程执行一些特殊的工作。
CyclicBarrier是功能强大的工具类,不过下面我们通过一个简单的例子来感受一下它的部分功能
上个星期我和朋友去吃自助,然后服务员说你们人太少了,要等其他人一起拼桌orz…,下面我们就把我和朋友当作线程,把服务员当作栅栏来应用一下CyclicBarrier]
public class CellularTest { private final CyclicBarrier cyclicBarrier; public CellularTest(int count) { cyclicBarrier = new CyclicBarrier(count, new Runnable() { @Override public void run() { System.out.println("有三个人到了,将他们带到餐桌"); } }); } public CyclicBarrier getCyclicBarrier() { return cyclicBarrier; } public static void main(String[] args) { CellularTest cellularTest = new CellularTest(3); for(int i = 0; i < 9; ++i) { Thread thread = new Thread() { @Override public void run() { try { System.out.println(this.getName() + " 等待服务员发话"); cellularTest.getCyclicBarrier().await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }; thread.start(); } } } /** Thread-0 等待服务员发话 Thread-1 等待服务员发话 Thread-2 等待服务员发话 有三个人到了,将他们带到餐桌 Thread-3 等待服务员发话 Thread-4 等待服务员发话 Thread-5 等待服务员发话 有三个人到了,将他们带到餐桌 Thread-6 等待服务员发话 Thread-7 等待服务员发话 Thread-8 等待服务员发话 有三个人到了,将他们带到餐桌 */