Juc并发编程15——循环屏障CyclickBarrier使用与源码剖析(上)

简介: 1.循环屏障的使用

1.循环屏障的使用

如果打一场游戏,必须等待游戏的玩家足够以后才开始,并且为了公平,所有玩家必须同时进入游戏。循环屏障CyclickBarrier就是为了解决这种场景而设计的

假如现在游戏需要10人才能开始,并且所有玩家需要同时进入游戏。我们可以这样实现

     public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(10, () -> {
            System.out.println("Ready to start,please prepare...");
        });
        for (int i = 0; i < 10; i++) {
            final int  finalI = i;
            new Thread(()->{
                try {
                    Thread.sleep((long) (2000 * new Random().nextDouble()));
                    System.out.println("Player:" + finalI + " prepared,"+ barrier.getNumberWaiting()+ "/10");
                    barrier.await();
                    System.out.println("Player:" + finalI + " Join Game...");
                } catch (InterruptedException | BrokenBarrierException exception) {
                    exception.printStackTrace();
                }
            }).start();
        }
    }

输出结果如下。


可以看到循环屏障会不断的阻挡线程,知道线程数量足够多时,再一起冲破线程屏障。并且在冲破屏障后,可以执行屏障创建时指定的任务。

屏障是可以循环使用的,在被冲破后,会重新开始计数,继续阻挡后续的线程。比如我们将屏障的初始容量设置为5,看看执行结果。

  public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(5, () -> {
            System.out.println("Ready to start,please prepare...");
        });
        for (int i = 0; i < 10; i++) {
            final int  finalI = i;
            new Thread(()->{
                try {
                    Thread.sleep((long) (2000 * new Random().nextDouble()));
                    System.out.println("Player:" + finalI + " prepared,"+ barrier.getNumberWaiting()+ "/5");
                    barrier.await();
                    System.out.println("Player:" + finalI + " Join Game...");
                } catch (InterruptedException | BrokenBarrierException exception) {
                    exception.printStackTrace();
                }
            }).start();
        }
    }

执行结果如下。

当然,除了自动清零,我们也可以将循环屏障手动置零

 public static void main(String[] args) throws InterruptedException {
        CyclicBarrier barrier = new CyclicBarrier(5, () -> {
            System.out.println("Ready to start,please prepare...");
        });
        for (int i = 0; i < 3; i++) {
            new Thread(()->{
                try {
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException exception) {
                    exception.printStackTrace();
                }
            }).start();
        }
        Thread.sleep(500);
        System.out.println(barrier.getNumberWaiting());
        barrier.reset();
        System.out.println(barrier.getNumberWaiting());
    }

执行结果如下。报了BrokenBarrierException,这是因为在循环屏障数达到3以后,还没有冲破屏障,我们就将循环屏障的计数清零了,之前处于等待状态的线程全部被中断,屏障被破坏了。

要是处于等待状态的线程被中断了呢?循环屏障的计数会不会自动减少

 public static void main(String[] args)  {
        CyclicBarrier barrier = new CyclicBarrier(5, () -> {
            System.out.println("Ready to start,please prepare...");
        });
        Runnable r = ()->
        {
                try {
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException exception) {
                    exception.printStackTrace();
                }
        };
       Thread t = new Thread(r);
       t.start();
       t.interrupt();
       new Thread(r).start();
    }

其结果如下。

第一个异常那个信息很好理解,是异常中断。第二个异常信息是因为屏障里的线程被取消,导致本轮屏障被破坏了。可以这么理解,约了三个朋友一起打麻将,结果有一个坑爹的队友临时爽约了,那他一个人就导致这局麻将组不成了。当然,我们还可以重新组局,我们也可以使用reset对屏障重新计数。

 public static void main(String[] args) throws InterruptedException {
        CyclicBarrier barrier = new CyclicBarrier(5, () -> {
            System.out.println("Ready to start,please prepare...");
        });
        Runnable r = ()->
        {
                try {
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException exception) {
                    exception.printStackTrace();
                }
        };
       Thread t = new Thread(r);
       t.start();
       t.interrupt();
       Thread.sleep(500); // 等待中断结束
       barrier.reset();
       new Thread(r).start();
    }


执行结果如下。

大家是不是有种感觉,CountdownLatchCyclickBarrier还挺相似的。我们来总结下他们的区别。

  • CountdownLatch
    一次性的,仅仅可以使用一次
    多个线程等待指定数量的其它线程完成任务的同步工具
  • CyclickBarrier
    可以重复使用
    多个线程在同一个时间开始执行的工具
相关文章
|
2月前
|
安全 Java API
JAVA并发编程JUC包之CAS原理
在JDK 1.5之后,Java API引入了`java.util.concurrent`包(简称JUC包),提供了多种并发工具类,如原子类`AtomicXX`、线程池`Executors`、信号量`Semaphore`、阻塞队列等。这些工具类简化了并发编程的复杂度。原子类`Atomic`尤其重要,它提供了线程安全的变量更新方法,支持整型、长整型、布尔型、数组及对象属性的原子修改。结合`volatile`关键字,可以实现多线程环境下共享变量的安全修改。
|
2月前
|
Java
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
本文介绍了拼多多面试中的模拟拼团问题,通过使用 `CyclicBarrier` 实现了多人拼团成功后提交订单并支付的功能。与之前的 `CountDownLatch` 方法不同,`CyclicBarrier` 能够确保所有线程到达屏障点后继续执行,并且屏障可重复使用。文章详细解析了 `CyclicBarrier` 的核心原理及使用方法,并通过代码示例展示了其工作流程。最后,文章还提供了 `CyclicBarrier` 的源码分析,帮助读者深入理解其实现机制。
|
2月前
|
存储 Java
JAVA并发编程AQS原理剖析
很多小朋友面试时候,面试官考察并发编程部分,都会被问:说一下AQS原理。面对并发编程基础和面试经验,专栏采用通俗简洁无废话无八股文方式,已陆续梳理分享了《一文看懂全部锁机制》、《JUC包之CAS原理》、《volatile核心原理》、《synchronized全能王的原理》,希望可以帮到大家巩固相关核心技术原理。今天我们聊聊AQS....
|
6月前
|
存储 安全 Java
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
102 3
|
6月前
|
并行计算 安全 Java
CyclicBarrier(循环屏障)源码解读与使用
CyclicBarrier(循环屏障)源码解读与使用
|
设计模式 安全 Java
JUC第十二讲:JUC锁 - 看不懂锁核心类 AQS 原理来打我
JUC第十二讲:JUC锁 - 看不懂锁核心类 AQS 原理来打我
|
缓存 监控 安全
JUC并发编程之线程锁(一)
1.ReentrantLock(互斥锁)、2.ReentRantReaderWriterLock(互斥读写锁)、3.StampedLock(无障碍锁)、4.Condition(自定义锁)、5.LockSupport
75 0
|
安全 调度
【并发编程】synchronized底层原理及对象锁和类锁实践
【并发编程】synchronized底层原理及对象锁和类锁实践
【并发编程】synchronized底层原理及对象锁和类锁实践
|
SpringCloudAlibaba 安全 Java
JUC并发编程(二):线程相关知识点
实现编发编程的主要手段就是多线程。线程是操作系统里的一个概念。接下来先说说两者的定义、联系与区别。
77 0
深入理解JUC:第五章:CyclicBarrier循环栅栏
深入理解JUC:第五章:CyclicBarrier循环栅栏
112 0
深入理解JUC:第五章:CyclicBarrier循环栅栏