1.同步屏障
同步屏障允许一组线程彼此相互等待,直到抵达某个公共的屏障点。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。
举个简单的例子就是:旅游团带着一帮人参观景点,规定在下一个景点A处集合,于是导游就在景点A等着大家,导游就是这个集合点或者说屏障,直到所有的游客集合完毕,导游才会带着大家继续参观下一个景点B.
类java.util.concurrent.CyclicBarrier实现了同步屏障。
2. 同步屏障适用场景
CyclicBarrier可以用于多线程计算数据,最后合并计算结果的场景。
3.常用方法
(1)CyclicBarrier(int parties)构造函数
初始化一个包含指定parties数目的CyclicBarrier实例。如果parties的值小于1,构造函数就会抛出IllegalArgumentException。
(2)CyclicBarrier(int parties, Runnable barrierAction)构造函数
初始化一个包含指定parties数目的线程以及一旦跨越屏障就会执行的barrierAction. 也就是说当最后一个线程到达一个屏障点时,就会马上执行barrierAction. 这个Runnable适用于在任意线程继续执行之前更新共享状态。
如果parties的值小于1,构造函数就会抛出IllegalArgumentException。若把barrierAction设为null,那么当跨越屏障时,就没有runnable可供执行。
(3)int await()
每个线程调用await(),表示我已经到达屏障点,然后当前线程被阻塞。如果调用线程是最后一条到达的线程,并且构造函数中提供了一个非空的barrierAction,这条线程就会在允许其他线程继续执行之前率先执行这个runnable。该方法会返回调用线程的到达索引,getParties-1代表第一条到达的线程,0代表最后一条到达的线程。
(4)int await(long timeout, TimeUnit unit)
指定调用线程愿意等待的时长,其他的跟上一个方法相同。当线程在等待中超时,该方法会抛出java.util.concurrent.TimeoutException。
(5)int getNumberWaiting()
返回当前在同步屏障上等待的线程数目。
(6)int getParties()
返回需要跨越同步屏障的线程数目。
(7)boolean isBroken()
当一条或多条线程由于在同步屏障创建或上次重置之后,中断或超时从而打破同步屏障,又或者因为一个异常导致barrier action失败时,返回true;否则返回false。
(8)void reset()
把同步屏障重置到其原始状态。
4.示例
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CyclicBarrierTest {
public static void main(String[] args)
{
CyclicBarrier barrier = new CyclicBarrier(3);
Runnable r = new Runnable() {
@Override
public void run()
{
try {
Thread.sleep(new Random().nextInt(10000));
String name = Thread.currentThread().getName();
System.out.println(name + "即将到达,当前已有"+(barrier.getNumberWaiting()+1)+"条线程已经到达!"+(barrier.getNumberWaiting()==2?"都到齐了,继续走":"正在等待"));
barrier.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i = 0; i < 3; i++)
{
executorService.submit(r);
}
executorService.shutdown();
}
}
运行结果:
pool-1-thread-1即将到达,当前已有1条线程已经到达!正在等待
pool-1-thread-2即将到达,当前已有2条线程已经到达!正在等待
pool-1-thread-3即将到达,当前已有3条线程已经到达!都到齐了,继续走
5. CountDownLatch与CountDownLatch的比较
(1)CountDownLatch是把主干线程挂起,在任务线程中进行倒数计数,直到任务线程执行完才唤醒主干线程继续执行;
CyclicBarrier是把任务线程挂起,直到所有任务线程执行到屏障处再放行继续执行;
(2)CountDownLatch达到屏障放行标准后放行的是主干线程;
CyclicBarrier达到屏障放行标准后放行的是任务线程,并且还会额外地触发一个达到标准后执行的响应线程;