zoukankan      html  css  js  c++  java
  • java并发编程JUC第十篇:CyclicBarrier线程同步

    在之前的文章中已经为大家介绍了java并发编程的工具:BlockingQueue接口、ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue、BlockingDeque接口、ConcurrentHashMap、CountDownLatch,本文为系列文章第十篇。

    java.util.concurrent.CyclicBarrier提供了一种多线程彼此等待的同步机制,可以把它理解成一个障碍,所有先到达这个障碍的线程都将将处于等待状态,直到所有线程都到达这个障碍处,所有线程才能继续执行。

    举个例子:CyclicBarrier的同步方式有点像朋友们约好了去旅游,在景点入口处集合,这个景点入口就是一个Barrier障碍,等待大家都到了才一起进入景点游览参观。 进入景点后大家去爬山,有的人爬得快,有的人爬的慢,大家约好了山顶集合,所以山顶就又是一个Barrier障碍,等待大家都到了山顶才一起下山。

    下面是一张图来说明这个问题。

    每个线程通过调用await(),在CyclicBarrier障碍处“彼此等待”,一旦所有的线程都到达了CyclicBarrier(都调用了CyclicBarrier方法),所有的线程将一起再次被唤醒继续执行。

    1.创建CyclicBarrier障碍

    当创建CyclicBarrier的时候,需要指定需要控制多少个线程同步。比如下面的CyclicBarrier设置为控制2个线程同步。

    CyclicBarrier barrier = new CyclicBarrier(2);
    

    2. 在CyclicBarrier障碍处等待

    通过调用CyclicBarrier的await()方法进入等待状态,通常在线程完成自己的阶段性任务之后调用该方法。

    barrier.await();
    

    CyclicBarrier也提供了另一种方法指定等待超时的时间,当等待时间大于超时时间之后,即使还有其他的线程没调用await方法,该线程将自动唤醒继续执行。(朋友们约好了去旅游,等了10分钟你还不来,我就自己先去了)。

    barrier.await(10, TimeUnit.SECONDS);
    

    The waiting threads waits at theCyclicBarrieruntil either:

    CyclicBarrier处等待的线程被释放,继续执行的条件(满足下面的任一条件即可)

    • 最后到达的线程调用了await() 方法
    • 该线程被另一个线程打断(另一个线程调用其interrupt()方法)。
    • 另一个处于等待状态的线程被打断
    • 另一个处于等待状态的线程在CyclicBarrier处等待时超时。
    • 某个外部线程调用了CyclicBarrier.reset()拆除障碍。

    3. CyclicBarrier Action

    CyclicBarrier Action 相对不太好理解,可以把它理解为障碍自身的行为。该Action动作是一个线程,所有的线程都到达障碍之后,该线程将被执行。

    Runnable      barrierAction =   创建线程;
    CyclicBarrier barrier  = new CyclicBarrier(2, barrierAction);
    

    如果这段代码仍然无法理解CyclicBarrier Action的作用,看下面的例子。

    4. CyclicBarrier 例子

    下面的代码演示了如何使用CyclicBarrier进行线程同步:

    Runnable barrier1Action = new Runnable() {
        public void run() {
            System.out.println("障碍1集合成功了,所有人都到了景点门口 ");
        }
    };
    Runnable barrier2Action = new Runnable() {
        public void run() {
            System.out.println("障碍2集合成功了,所有人都到了山顶");
        }
    };
    
    //障碍1 景点门口
    CyclicBarrier barrier1 = new CyclicBarrier(2, barrier1Action);
    //障碍2 山顶
    CyclicBarrier barrier2 = new CyclicBarrier(2, barrier2Action);
    
    //旅游计划,阶段目标一:景点门口集合
    CyclicBarrierRunnable barrierRunnable1 =
            new CyclicBarrierRunnable(barrier1, barrier2);
    //旅游计划,阶段目标二:爬山到山顶集合
    CyclicBarrierRunnable barrierRunnable2 =
            new CyclicBarrierRunnable(barrier1, barrier2);
    
    new Thread(barrierRunnable1).start();  //游客A,Thread-0
    new Thread(barrierRunnable2).start();  //游客B,Thread-1
    

    下面是一个线程类CyclicBarrierRunnable,启动一个就代表一个游客

    public class CyclicBarrierRunnable implements Runnable{
    
        CyclicBarrier barrier1 = null;  //障碍1
        CyclicBarrier barrier2 = null;  //障碍2
    
        public CyclicBarrierRunnable( CyclicBarrier barrier1,CyclicBarrier barrier2) {
            this.barrier1 = barrier1;
            this.barrier2 = barrier2;
        }
    
        public void run() {
            try {
                Thread.sleep(1000);  //这里写出发去景点的过程代码
                System.out.println(Thread.currentThread().getName() +
                                    " 到达景点门口");
                this.barrier1.await();
    
                Thread.sleep(1000);  //这里写爬山的过程代码
                System.out.println(Thread.currentThread().getName() +
                                    " 爬山爬到山顶");
                this.barrier2.await();
    
                System.out.println(Thread.currentThread().getName() +
                                    " 玩的不错,下山回家!");
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
    

    下面的输出是上文代码的执行打印结果,多执行几次上文的代码会发现Thread-0 和 Thread-1 在障碍1和障碍2处的到达先后顺序是不确定的,但是总是先到的等后到的再继续执行。

    Thread-0 到达景点门口
    Thread-1 到达景点门口
    障碍1集合成功了,所有人都到了景点门口
    Thread-1 爬山爬到山顶
    Thread-0 爬山爬到山顶
    障碍2集合成功了,所有人都到了山顶
    Thread-0  玩的不错,下山回家!
    Thread-1  玩的不错,下山回家!
    

    欢迎关注我的博客,里面有很多精品合集

    • 本文转载注明出处(必须带连接,不能只转文字):字母哥博客

    觉得对您有帮助的话,帮我点赞、分享!您的支持是我不竭的创作动力! 。另外,笔者最近一段时间输出了如下的精品内容,期待您的关注。

  • 相关阅读:
    Java实现 LeetCode 50 Pow(x,n)
    Java实现 LeetCode 50 Pow(x,n)
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 47 全排列 II(二)
    Java实现 LeetCode 47 全排列 II(二)
  • 原文地址:https://www.cnblogs.com/zimug/p/14902954.html
Copyright © 2011-2022 走看看