Java并发——同步屏障

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达到屏障放行标准后放行的是任务线程,并且还会额外地触发一个达到标准后执行的响应线程;

猜你喜欢

转载自blog.csdn.net/zhm1563550235/article/details/84442879