同步机制之--java CyclicBarrier 循环栅栏

CyclicBarrier介绍
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作很有用。 

CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环的barrier(栅栏)

  CyclicBarrier类似于CountDownLatch也是个计数器, 不同的是CyclicBarrier数的是调用了CyclicBarrier.await()进入等待的线程数, 当线程数达到了CyclicBarrier初始时规定的数目时,所有进入等待状态的线程被唤醒并继续。 CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。 CyclicBarrier初始时还可带一个Runnable的参数,此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。

构造方法摘要
CyclicBarrier(int parties) 
          创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在每个 barrier 上执行预定义的操作。
CyclicBarrier(int parties, Runnable barrierAction) 
          创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行

 

 

方法摘要
int await() 
          在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
int await(long timeout, TimeUnit unit) 
          在所有参与者都已经在此屏障上调用 await 方法之前,将一直等待。
int getNumberWaiting() 
          返回当前在屏障处等待的参与者数目。
int getParties() 
          返回要求启动此 barrier 的参与者数目。
boolean isBroken() 
          查询此屏障是否处于损坏状态。
void reset() 
          将屏障重置为其初始状态。

 

栅栏类似闭锁,但是它们是有区别的.

1.闭锁用来等待事件,而栅栏用于等待其他线程.什么意思呢?就是说闭锁用来等待的事件就是countDown事件,只有该countDown事件执行后所有之前在等待的线程才有可能继续执行;而栅栏没有类似countDown事件控制线程的执行,只有线程的await方法能控制等待的线程执行.

2.CyclicBarrier强调的是n个线程,大家相互等待,只要有一个没完成,所有人都得等着。

满足以下任何条件都可以让等待 CyclicBarrier 的线程释放:

  • 最后一个线程也到达 CyclicBarrier(调用 await())
  • 当前线程被其他线程打断(其他线程调用了这个线程的 interrupt() 方法)
  • 其他等待栅栏的线程被打断
  • 其他等待栅栏的线程因超时而被释放
  • 外部线程调用了栅栏的 CyclicBarrier.reset() 方法

 

示例1:张孝祥视频学习笔记:

CyclicBarrier 表示大家彼此等待,大家集合好后才开始出发,分散活动后又在i指定地点集合碰面,这就好比整个公司的人员利用周末时间集体郊游一样,先各自从家出发到公司集合后,再同时出发到公园游玩,在指定地点集合后再同时开始就餐……

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) {
        ExecutorService service = Executors.newCachedThreadPool();
        final CyclicBarrier cb = new CyclicBarrier(3);// 创建CyclicBarrier对象并设置3个公共屏障点
        System.out.println("创建子线程开始");
        for (int i = 0; i < 3; i++) {
            Runnable runnable = new Runnable() {
                public void run() {
                    try {
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println(
                                "线程" + Thread.currentThread().getName() + "即将到达集合地点1,当前已有" + (cb.getNumberWaiting() + 1)
                                        + "个已到达" + (cb.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候"));
                        try {
                            cb.await();
                        } catch (BrokenBarrierException e) {
                            e.printStackTrace();
                        }
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println(
                                "线程" + Thread.currentThread().getName() + "即将到达集合地点2,当前已有" + (cb.getNumberWaiting() + 1)
                                        + "个已到达" + (cb.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候"));
                        try {
                            cb.await();
                        } catch (BrokenBarrierException e) {
                            e.printStackTrace();
                        }
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println(
                                "线程" + Thread.currentThread().getName() + "即将到达集合地点3,当前已有" + (cb.getNumberWaiting() + 1)
                                        + "个已到达" + (cb.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候"));
                        try {
                            cb.await();
                        } catch (BrokenBarrierException e) {
                            e.printStackTrace();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            service.execute(runnable);
            System.out.println("子线程 "+ i + "提交到线程池中");
        }
        System.out.println("所有子线程提交完毕,线程池关闭,但线程池关闭的真实时间为所有子线程都执行完毕后");
        service.shutdown();//线程池的状态则立刻变成SHUTDOWN状态,以后不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionException异常。但是,此时线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。 与它相似的还有一个shutdownNow(),它通过调用Thread.interrupt来实现线程的立即退出。
    }
}

运行结果:

创建子线程开始
子线程 0提交到线程池中
子线程 1提交到线程池中
子线程 2提交到线程池中
所有子线程提交完毕,线程池关闭,但线程池关闭的真实时间为所有子线程都执行完毕后
线程pool-1-thread-1即将到达集合地点1,当前已有1个已到达正在等候
线程pool-1-thread-3即将到达集合地点1,当前已有2个已到达正在等候
线程pool-1-thread-2即将到达集合地点1,当前已有3个已到达都到齐了,继续走啊
线程pool-1-thread-2即将到达集合地点2,当前已有1个已到达正在等候
线程pool-1-thread-1即将到达集合地点2,当前已有2个已到达正在等候
线程pool-1-thread-3即将到达集合地点2,当前已有3个已到达都到齐了,继续走啊
线程pool-1-thread-1即将到达集合地点3,当前已有1个已到达正在等候
线程pool-1-thread-2即将到达集合地点3,当前已有2个已到达正在等候
线程pool-1-thread-3即将到达集合地点3,当前已有3个已到达都到齐了,继续走啊

 

示例2:并发测试时,让所有的请求在同一时间点发出去

public static void main(String[] args) throws Exception {
        // cycle connect
        int thread = 100;
        int count = 100;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(thread + 1);
        AtomicLong connectCount = new AtomicLong(0);
        for (int i = 0; i < thread; i++) {
            new requestThread().start();
        }
        try {
            cyclicBarrier.await();
            while (true) {
                System.out.println(new Date() + " Send message count : "
                        + connectCount.get());
                TimeUnit.SECONDS.sleep(1);
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        }

    }

 

posted on 2015-05-09 08:34  duanxz  阅读(1488)  评论(0编辑  收藏  举报