thread_CyclicBarrier回环栅栏

CyclicBarrier回环栅栏,字面意思是可循环使用(Cyclic)的屏障(Barrier)。通过它可以实现让一组线程等待至某个状态之后再全部同时执行。

它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。

叫做回环是因为当所有等待线程都被释放以后,
可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。

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

1.简单例子

    // 给一组线程到达一同步点,之前时被阻塞,直到最后一个线程到达障时,拦截的线程才会继续干活。
    @Test
    public void cyclicBarrier1Test() throws InterruptedException {
        // 参数表示屏障拦截的线程数量,
        CyclicBarrier c = new CyclicBarrier(2);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。
                    c.await();
                } catch (Exception e) {
                }
                System.out.println(1);
            }
        }).start();
        // 第二次到达,之前到达屏障的两个线程都不会继续执行。到达后任意个线程先执行,再执行下个
        try {
            c.await();
        } catch (Exception e) {
        }
        System.out.println(2);
    }
    返回
         1
         2
    @Test
    public void cyclicBarrier2Test() throws InterruptedException {
        // 在线程到达屏障时,优先执行 A 线程
        CyclicBarrier c = new CyclicBarrier(2, new A());
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    c.await();
                } catch (Exception e) {
                }
                System.out.println(1);
            }
        }).start();
        try {
            c.await();
        } catch (Exception e) {

        }
        System.out.println(2);
    }
    class A implements Runnable {
        @Override
        public void run() {
            System.out.println(3);
        }
    }
返回

       3
       1
       2

2.处理复杂

CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
  CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。比如以下代码执行完之后会返回true。
    /*
    @Test
    public void cyclicBarrier3Test() throws InterruptedException, BrokenBarrierException {
        CyclicBarrier c = new CyclicBarrier(2);
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    c.await();
                } catch (Exception e) {
                }
            }
        });
        thread.start();
        thread.interrupt();
        try {
            c.await();
        } catch (Exception e) {
            System.out.println("isBroken " + c.isBroken());
        }
    }

3. 让线程同时开始

    @Test
    public void cyclicBarrier4Test() throws IOException, InterruptedException {
        CyclicBarrier barrier = new CyclicBarrier(4);
        ExecutorService executor = Executors.newFixedThreadPool(3);
        executor.submit(new Thread(new Runner(barrier, "1号选手")));
        executor.submit(new Thread(new Runner(barrier, "2号选手")));
        executor.submit(new Thread(new Runner(barrier, "3号选手")));

        executor.shutdown();
        try {
            barrier.await();
            System.out.println("isBroken ");

        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}

class Runner implements Runnable {
    // 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)
    private CyclicBarrier barrier;

    private String name;

    public Runner(CyclicBarrier barrier, String name) {
        super();
        this.barrier = barrier;
        this.name = name;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(100 * (new Random()).nextInt(8));
            System.out.println(name + " 准备好了...");
            // barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
            barrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println(name + " 起跑!");
    }
}

4.CyclicBarrier和CountDownLatch的区别
   CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
   CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。比如以下代码执行完之后会返回true。

原文地址:https://www.cnblogs.com/dengzy/p/5800683.html