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
    可以重复使用
    多个线程在同一个时间开始执行的工具
目录
打赏
0
0
0
0
4
分享
相关文章
JUC并发编程之中断机制
在并发编程中,一个线程的执行可能会被另一个线程打断,这种打断称为"中断"。中断是一种线程间的通信机制,它允许一个线程通知另一个线程,请求它停止当前的工作并进行一些其他的操作。JUC中的中断机制是通过interrupt()方法实现的。
108 1
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
本文介绍了拼多多面试中的模拟拼团问题,通过使用 `CyclicBarrier` 实现了多人拼团成功后提交订单并支付的功能。与之前的 `CountDownLatch` 方法不同,`CyclicBarrier` 能够确保所有线程到达屏障点后继续执行,并且屏障可重复使用。文章详细解析了 `CyclicBarrier` 的核心原理及使用方法,并通过代码示例展示了其工作流程。最后,文章还提供了 `CyclicBarrier` 的源码分析,帮助读者深入理解其实现机制。
JAVA并发编程AQS原理剖析
很多小朋友面试时候,面试官考察并发编程部分,都会被问:说一下AQS原理。面对并发编程基础和面试经验,专栏采用通俗简洁无废话无八股文方式,已陆续梳理分享了《一文看懂全部锁机制》、《JUC包之CAS原理》、《volatile核心原理》、《synchronized全能王的原理》,希望可以帮到大家巩固相关核心技术原理。今天我们聊聊AQS....
【多线程面试题二十五】、说说你对AQS的理解
这篇文章阐述了对Java中的AbstractQueuedSynchronizer(AQS)的理解,AQS是一个用于构建锁和其他同步组件的框架,它通过维护同步状态和FIFO等待队列,以及线程的阻塞与唤醒机制,来实现同步器的高效管理,并且可以通过实现特定的方法来自定义同步组件的行为。
【多线程面试题二十五】、说说你对AQS的理解
JUC第二讲:Java并发理论基础:Java内存模型(JMM)与线程
JUC第二讲:Java并发理论基础:Java内存模型(JMM)与线程
127 0
JUC并发编程之线程锁(一)
1.ReentrantLock(互斥锁)、2.ReentRantReaderWriterLock(互斥读写锁)、3.StampedLock(无障碍锁)、4.Condition(自定义锁)、5.LockSupport
86 0
并发编程-15并发容器(J.U.C)核心 AbstractQueuedSynchronizer 抽象队列同步器AQS介绍
并发编程-15并发容器(J.U.C)核心 AbstractQueuedSynchronizer 抽象队列同步器AQS介绍
153 0
Java多线程之线程同步(解决线程安全问题)
保证多个线程同时对某一对象或资源操作时不会出现问题
180 0
Java多线程之线程同步(解决线程安全问题)