Java并发工具类 CyclicBarrier

Java并发工具类需要依赖对于Java锁机制的理解,尤其是AQS,可以参见前文:
Java锁机制浅析(一)
Java锁机制浅析(二)之AQS
Java并发工具类 CountDownLatch

3.2 CyclicBarrier 线程屏障

允许一组线程全部等待彼此达到共同屏障点的同步辅助类。 它可以在等待的线程被释放之后重新使用。想象一下导弹部队饱和攻击哈哈,~所有导弹发射车到达指定位置后,开始齐射导弹。。

3.2.1主要API

方法说明
CyclicBarrier(int parties)创建一个指定数量的同步屏障对象。
CyclicBarrier(int parties,Runnable action)创建一个指定数量的同步屏障对象,当屏障破碎时执行指定的Runnable任务。
int await()阻塞调用线程直到所有线程到达屏障。
int await(long timeout,TimeUnit unit)阻塞调用线程直到所有线程到达屏障或者超时。
boolean isBroken()查询屏障是否处于破碎状态。
void reset()将屏障重置为初始状态。
int getNumberWaiting()获取正在等待屏障破碎的线程数量。

3.2.2 Demo

CyclicBarrier和CountDownLatch比较类似,都可以用在一些 条件等待–>条件达成–>唤醒等待 场景中。但是二者区别也比较明显:

  • CyclicBarrier 是通过正向计数的方式让一方的多个线程在同一个障碍点等待彼此,当最后一个线程调用await()时,屏障破碎,所有等待的线程自动激活。通过主动调用屏障的reset()方法重置后,可以重复使用。
  • CountDownLatch则是通过递减计数方式让一方的线调用await()函数进行等待,再由另外一方的线程通过countDown()逐步让计数归零后激活等待一方的线程。并且是一次性的,无法重复使用。
3.2.2.1 模拟导弹齐射
public class CyclicBarrierDemo {
    public static void main(String[] args)throws Exception{
        int N = 4;
        CyclicBarrier barrier = new CyclicBarrier(N,()->{
            System.out.println("所有导弹车到达预定地点,导弹齐射开始!");
        });

        for (int i = 1; i <= N; i++) {
            MissileLauncher er = new MissileLauncher(i,barrier);
            Thread thread = new Thread(er);
            thread.start();
        }
    }
    //模拟发射车
    static class MissileLauncher implements Runnable {
        private int no ;
        private CyclicBarrier barrier;

        MissileLauncher(int no,CyclicBarrier barrier) {
            this.no = no;
            this.barrier = barrier;
        }

        public void run() {
            try {
                long l = ThreadLocalRandom.current().nextLong(8000);
                Thread.sleep(l); //模拟不同时间到达
                System.out.println(String.format("发射车[%d]号,%s 到达指定地点!",no, LocalTime.now()));
                barrier.await();
                System.out.println(String.format("编号%d发射车发射!",no));
            } catch (InterruptedException ex) {
                return;
            } catch (BrokenBarrierException ex) {
                return;
            }
        }
    }
}

导弹车开始集结!
发射车[4]号,17:08:15.102 到达指定地点!
发射车[1]号,17:08:17.038 到达指定地点!
发射车[3]号,17:08:17.738 到达指定地点!
发射车[2]号,17:08:18.021 到达指定地点!
所有导弹车到达预定地点,导弹齐射开始!
编号2发射车发射!
编号4发射车发射!
编号1发射车发射!
编号3发射车发射!

3.2.2.1 模拟多轮次齐射
public class MultiRoundMissileSalvo {
    public static void main(String[] args)throws Exception{
        int N = 4;
        CyclicBarrier barrier = new CyclicBarrier(N,()->{
            System.out.println("所有导弹车到达预定地点,导弹齐射开始!");
        });

        for (int round = 1; round <= 3; round++) {
            System.out.println(String.format("第【%d】轮导弹发射:",round));
            CountDownLatch cdt = new CountDownLatch(N);
            for (int i = 0; i < N; i++) {
                MissileLauncher er = new MissileLauncher(i,barrier,cdt);
                Thread thread = new Thread(er);
                thread.start();
            }
            cdt.await();
            barrier.reset();
        }
    }
    //模拟发射车
    static class MissileLauncher implements Runnable {
        private int no ;
        private CyclicBarrier barrier;
        private CountDownLatch cdt;
        MissileLauncher(int no,CyclicBarrier barrier ,CountDownLatch cdt) {
            this.no = no;
            this.barrier = barrier;
            this.cdt = cdt;
        }
        public void run() {
            try {
                long l = ThreadLocalRandom.current().nextLong(8000);
                Thread.sleep(l); //模拟不同时间到达
                System.out.println(String.format("%s: 发射车[%d]号-到达指定地点!", LocalTime.now(),no));
                barrier.await();
                System.out.println(String.format("%s: 发射车[%d]号-发射!",LocalTime.now(),no));
                this.cdt.countDown();
            } catch (Exception ex) {
                ex.printStackTrace();
            } 
        }
    }
}

3.2.3 源码

await()await(long timeout, TimeUnit unit)都是调用dowait(boolean timed, long nanos)方法实现。


	/** The lock for guarding barrier entry */
    private final ReentrantLock lock = new ReentrantLock();
    /** Condition to wait on until tripped */
    private final Condition trip = lock.newCondition();
    /**
     * 如果加上当前线程到达屏障指定数量,
     * 	 1:由当前线程执行指定的BarrierAction
     *   2:唤醒在Condition队列阻塞的其他线程并重置屏障
     * 反之,则将通过内部持有的Condition队列阻塞当前线程。
     */
    private int dowait(boolean timed, long nanos)
            throws InterruptedException, BrokenBarrierException,
            TimeoutException {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            final Generation g = generation;

            //broken中断支持,在最后一个线程到达之前都可通过broken方法进行中断
            if (g.broken)
                throw new BrokenBarrierException();
            //支持线程interrupted中断
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
            //最后的线程到达屏障后执行action,通过。nextGeneration()唤醒其他线程并重置屏障
            //在finally块检测action是否正常执行完成,如果action执行异常未能完成,则中断屏障
            int index = --count;
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;
                    //唤醒阻塞线程
                    nextGeneration();
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }
            //线程数未到达屏障指定count时
            for (;;) {
                try {
                    //通过Condition的await方法阻塞当前线程,同时将线程加入等待队列。
                    if (!timed)
                        trip.await();
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        Thread.currentThread().interrupt();
                    }
                }

                //阻塞线程被唤醒后首先检查屏障是否中断。
                if (g.broken)
                    throw new BrokenBarrierException();
                //检查是否被重置
                if (g != generation)
                    return index;
                //检查是否超时
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }
    //唤醒在Condition上阻塞的线程并重置屏障。
    private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();
        // set up next generation
        count = parties;
        generation = new Generation();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值