代码之家  ›  专栏  ›  技术社区  ›  daydreamer

Java并发:倒计时锁存与循环屏障

  •  143
  • daydreamer  · 技术社区  · 14 年前

    我读了一遍 java.util.concurrent API ,并发现

    • CountDownLatch :一种同步辅助程序,允许一个或多个线程等待在其他线程中执行的一组操作完成。
    • CyclicBarrier :一种同步辅助工具,允许一组线程彼此等待到达公共屏障点。

    在我看来,两个人都是平等的,但我相信还有很多。

    例如,在 CoundownLatch, the countdown value could not be reset, that can happen in the case of CyclicBarrier .

    这两者还有什么区别吗?
    什么是 use cases 有人想重置倒计时的值吗?

    14 回复  |  直到 12 年前
        1
  •  131
  •   Jonathan Holloway    14 年前

    一个主要的区别是 CyclicBarrier 接受一个(可选)可运行任务,该任务在满足公共屏障条件后运行。

    它还允许您获取在屏障处等待的客户机数量和触发屏障所需的数量。一旦触发,屏障将重置并可再次使用。

    对于简单的用例-服务启动等。。。倒计时可以。循环载体对于更复杂的协调任务很有用。这类事情的一个例子是并行计算——其中有多个子任务参与计算——类似于 MapReduce .

        2
  •  125
  •   Kim    14 年前

    还有另一个区别。

    当使用 CyclicBarrier ,假设您指定触发屏障的等待线程数。如果指定5,则必须至少有5个线程可以调用 await() .

    当使用 CountDownLatch ,则指定对 countDown() 这将导致释放所有等待的线程。这意味着您可以使用 倒计时锁 只有一根线。

    “你为什么要那样做?”,你可以说。想象一下,您正在使用一个由执行回调的其他人编写的神秘API。您希望其中一个线程等待某个回调被调用多次。您不知道将调用回调的线程。在这种情况下,a 倒计时锁 是完美的,但是我想不出用 篱栅 (事实上,我可以,但这需要超时。。。恶心!).

    我只是希望 倒计时锁 可以重置!

        3
  •  40
  •   Chirlo    12 年前

    有一点还没有人提到,在 CyclicBarrier ,如果某个线程有问题(超时、中断…),则所有其他已到达的线程 await() 有个例外。见Javadoc:

    CyclicBarrier对失败的同步尝试使用全中断或无中断模型:如果线程由于中断、失败或超时而过早地离开了一个屏障点,在该屏障点等待的所有其他线程也将通过BrokenBarrierException(或者InterruptedException,如果它们也在大约同一时间被中断的话)异常地离开。

        4
  •  22
  •   Piotrek Hryciuk Thadeuse    10 年前

    我认为JavaDoc已经明确地解释了这些差异。 大多数人都知道倒计时锁不能重置,然而,CyclicBarrier可以。但这并不是唯一的区别,或者可以将CyclicBarrier重命名为resetblecountdownlock。 我们应该从JavaDoc中描述的目标的角度来区分这些差异

    倒计时闩锁: 一种同步辅助程序,允许一个或多个线程等待在其它线程中执行的一组操作完成。

    自行车承运人: 一种同步辅助程序,允许一组线程相互等待到达一个公共的屏障点。

    在倒计时锁存中,有一个或多个线程正在等待一组 其他线程 完成。在这种情况下,有两种类型的线程,一种类型正在等待,另一种类型正在执行某些操作,在完成任务后,它们可能正在等待或刚刚终止。

    在CyclicBarrier中,只有一种线程,它们彼此等待,彼此相等。

        5
  •  14
  •   JohnnyO    14 年前

    主要的区别记录在Javadocs for countdownlock中。即:

    使用 给定计数。等待方法块 直到当前计数为零 由于调用了倒计时() 方法,然后等待 线程被释放 等待返回的后续调用 立刻。只有一次机会 现象——计数不能 重置。如果你需要一个版本 重置计数,考虑使用 自行车承运人。

    来源 1.6 Javadoc

        6
  •  12
  •   shams    12 年前

    倒计时锁存器用于一次性同步。在使用CountDownLatch时,任何线程都可以根据自己的喜好多次调用countDown()。调用await()的线程将被阻止,直到计数达到零,因为其他未阻止的线程调用了countDown()。这个 javadoc for CountDownLatch 国家:

    由于 调用countDown()方法,之后所有等待线程 被释放以及随后的等待返回调用 立刻。 ...

    另一个典型的用法是把一个问题分成N个部分, 用执行该部分的Runnable描述每个部分,然后 在锁存器上倒计时,并将所有可运行文件排队给执行器。 当所有子部件都完成时,协调线程将能够 经过等待。(当线程必须在 这样,就可以使用CyclicBarrier。)

    相反,循环屏障用于多个同步点,例如,如果一组线程正在运行循环/阶段计算,并且需要在开始下一个迭代/阶段之前进行同步。根据 javadoc for CyclicBarrier :

    屏障被称为循环屏障,因为它可以在 等待线程被释放。

    与CountDownLatch不同,对await()的每个调用都属于某个阶段,可能导致线程阻塞,直到属于该阶段的所有方都调用了await()。CyclicBarrier不支持显式的countDown()操作。

        7
  •  12
  •   Kevin Lee    11 年前

    这个问题已经得到了充分的回答,但是我想我可以通过发布一些代码来增加一些价值。

    为了说明循环势垒的行为,我制作了一些示例代码。障碍物一倾斜,就 自动 重新设置以便可以再次使用(因此它是“循环的”)。运行程序时,请注意只有在障碍物倾斜后才会触发打印输出“让我们玩”。

    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    
    public class CyclicBarrierCycles {
    
        static CyclicBarrier barrier;
    
        public static void main(String[] args) throws InterruptedException {
            barrier = new CyclicBarrier(3); 
    
            new Worker().start();
            Thread.sleep(1000);
            new Worker().start();
            Thread.sleep(1000);
            new Worker().start();
            Thread.sleep(1000);
    
            System.out.println("Barrier automatically resets.");
    
            new Worker().start();
            Thread.sleep(1000);
            new Worker().start();
            Thread.sleep(1000);
            new Worker().start();
        }
    
    }
    
    
    class Worker extends Thread {
        @Override
        public void run() {
            try {
                CyclicBarrierCycles.barrier.await();
                System.out.println("Let's play.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
    
        8
  •  7
  •   Mr.Q    7 年前

    当我研究闩锁和自行车运载器时,我想到了这个比喻。 循环载体 :假设一家公司有一个会议室。为了开始会议,一定数量的与会者必须参加会议(使之正式)。以下是正常会议与会者(员工)的代码

    class MeetingAtendee implements Runnable {
    
    CyclicBarrier myMeetingQuorumBarrier;
    
    public MeetingAtendee(CyclicBarrier myMileStoneBarrier) {
        this.myMeetingQuorumBarrier = myMileStoneBarrier;
    }
    
    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + " i joined the meeting ...");
            myMeetingQuorumBarrier.await();
            System.out.println(Thread.currentThread().getName()+" finally meeting stared ...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            System.out.println("Meeting canceled! every body dance <by chic band!>");
        }
     }
    }
    

    员工参加会议,等待其他人来开会。如果会议取消了,他也会退出:那么我们有老板怎么不喜欢等别人出现,如果他松开病人,他就取消会议。

    class MeetingAtendeeTheBoss implements Runnable {
    
    CyclicBarrier myMeetingQuorumBarrier;
    
    public MeetingAtendeeTheBoss(CyclicBarrier myMileStoneBarrier) {
        this.myMeetingQuorumBarrier = myMileStoneBarrier;
    }
    
    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + "I am THE BOSS - i joined the meeting ...");
            //boss dose not like to wait too much!! he/she waits for 2 seconds and we END the meeting
            myMeetingQuorumBarrier.await(1,TimeUnit.SECONDS);
            System.out.println(Thread.currentThread().getName()+" finally meeting stared ...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            System.out.println("what WHO canceled The meeting");
        } catch (TimeoutException e) {
            System.out.println("These employees waste my time!!");
        }
     }
    }
    

    在正常的一天,员工来开会等其他人来,如果有人不来,他们就得无限期地等待!在某个特别会议上,老板来了,他不喜欢等。(5个人需要开始开会,但只有老板来,还有一个热情的员工),所以他(生气地)取消了会议

    CyclicBarrier meetingAtendeeQuorum = new CyclicBarrier(5);
    Thread atendeeThread = new Thread(new MeetingAtendee(meetingAtendeeQuorum));
    Thread atendeeThreadBoss = new Thread(new MeetingAtendeeTheBoss(meetingAtendeeQuorum));
        atendeeThread.start();
        atendeeThreadBoss.start();
    

    输出:

    //Thread-1I am THE BOSS - i joined the meeting ...
    // Thread-0 i joined the meeting ...
    // These employees waste my time!!
    // Meeting canceled! every body dance <by chic band!>
    

    另一种情况是另一个外部线程(地震)取消会议(调用重置方法)。在这种情况下,所有等待的线程都会被异常唤醒。

    class NaturalDisasters implements Runnable {
    
    CyclicBarrier someStupidMeetingAtendeeQuorum;
    
    public NaturalDisasters(CyclicBarrier someStupidMeetingAtendeeQuorum) {
        this.someStupidMeetingAtendeeQuorum = someStupidMeetingAtendeeQuorum;
    }
    
    void earthQuakeHappening(){
        System.out.println("earth quaking.....");
        someStupidMeetingAtendeeQuorum.reset();
    }
    
    @Override
    public void run() {
        earthQuakeHappening();
     }
    }
    

    运行代码将导致有趣的输出:

    // Thread-1I am THE BOSS - i joined the meeting ...
    // Thread-0 i joined the meeting ...
    // earth quaking.....
    // what WHO canceled The meeting
    // Meeting canceled! every body dance <by chic band!>
    

    您也可以在会议室中添加一名秘书,如果召开会议,她将记录所有事情,但她不是会议的一部分:

    class MeetingSecretary implements Runnable {
    
    @Override
    public void run() {
            System.out.println("preparing meeting documents");
            System.out.println("taking notes ...");
     }
    }
    

    闩锁 :如果愤怒的老板想为公司客户举办展览,每件事都需要做好准备(资源)。我们为每个工人(线程)提供了一个待办事项列表,我们检查了待办事项列表(一些工人进行喷漆,另一些准备音响系统…)。当待办事项列表中的所有项目都完成(提供了资源)时,我们可以向客户敞开大门。

    public class Visitor implements Runnable{
    
    CountDownLatch exhibitonDoorlatch = null;
    
    public Visitor (CountDownLatch latch) {
        exhibitonDoorlatch  = latch;
    }
    
    public void run() {
        try {
            exhibitonDoorlatch .await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    
        System.out.println("customer visiting exebition");
     }
    }
    

    工人们如何准备展览:

    class Worker implements Runnable {
    
    CountDownLatch myTodoItem = null;
    
    public Worker(CountDownLatch latch) {
        this.myTodoItem = latch;
    }
    
    public void run() {
            System.out.println("doing my part of job ...");
            System.out.println("My work is done! remove it from todo list");
            myTodoItem.countDown();
     }
    }
    
        CountDownLatch preperationTodoList = new CountDownLatch(3);
    
        // exhibition preparation workers  
        Worker      electricalWorker      = new Worker(preperationTodoList);
        Worker      paintingWorker      = new Worker(preperationTodoList);
    
        // Exhibition Visitors 
        ExhibitionVisitor exhibitionVisitorA = new ExhibitionVisitor(preperationTodoList);
        ExhibitionVisitor exhibitionVisitorB = new ExhibitionVisitor(preperationTodoList);
        ExhibitionVisitor exhibitionVisitorC = new ExhibitionVisitor(preperationTodoList);
    
        new Thread(electricalWorker).start();
        new Thread(paintingWorker).start();
    
        new Thread(exhibitionVisitorA).start();
        new Thread(exhibitionVisitorB).start();
        new Thread(exhibitionVisitorC).start();
    
        9
  •  6
  •   igor.zh    8 年前

    一言以蔽之 ,只是为了理解 功能性 两者的区别:

    public class CountDownLatch {
        private Object mutex = new Object();
        private int count;
    
        public CountDownLatch(int count) {
            this.count = count;
        }
    
        public void await() throws InterruptedException {
            synchronized (mutex) {
                while (count > 0) {
                    mutex.wait();
                }
            }
        }
    
        public void countDown() {
            synchronized (mutex) {
                if (--count == 0)
                    mutex.notifyAll();
            }
    
        }
    }
    

    public class CyclicBarrier {
        private Object mutex = new Object();
        private int count;
    
        public CyclicBarrier(int count) {
            this.count = count;
        }
    
        public void await() throws InterruptedException {
            synchronized (mutex) {
                count--;
                while(count > 0)
                    mutex.wait();
                mutex.notifyAll();
            }
        }
    }
    

    当然,除了非阻塞、定时等待、诊断和以上答案中详细说明的所有功能之外。

    然而,在所提供的功能内,上述类是完全功能性的,并且与它们对应的名称相同。

    另一方面, CountDownLatch 的内部类子类 AQS ,而 CyclicBarrier 使用 ReentrantLock (我怀疑这可能是另一种方式,或者两者都可以使用AQS,或者两者都使用Lock——而不会降低性能效率)

        10
  •  4
  •   Brandon    13 年前

    在CyclicBarrier的情况下,只要所有子线程开始调用barrier.await(),Runnable就在barrier中执行。每个子线程中的barrier.await将花费不同长度的时间来完成,并且它们都在同一时间完成。

        11
  •  4
  •   Pramma    9 年前

    一个明显的区别是,只有N个线程可以在一个周期中等待N的CyclicBarrier的释放。但在N的倒计时锁存器上可以等待无限数量的线程。倒计时递减可以由一个线程N次或N个线程每次或组合进行。

        12
  •  3
  •   V Jo    8 年前

    倒计时锁 ,主线程等待其他线程完成其执行。在 篱栅 ,工作线程等待对方完成其执行。

    不能重复使用 倒计时锁 另一方面,实例一旦计数达到零并且闩锁打开 篱栅 一旦屏障被破坏,可通过重置屏障重新使用。

        13
  •  1
  •   yk42b    6 年前

    countdownlock是任何事物的倒计时;CyclicBarrier是线程的倒计时

    假设有5个工作线程和一个shipper线程,当工作线程生成100个项目时,shipper将把它们发送出去。

    对于倒计时锁,计数器可以在工人或物品上

    对于CyclicBarrier,计数器只能针对工人

    如果工人睡不着觉,物品倒计时锁,发货人可以发货;但是,使用CyclicBarrier,发货人永远不能被调用

        14
  •  0
  •   Hari Rao    6 年前

    @Kevin Lee和@Jon我尝试了使用可选Runnable的CyclicBarrier。看起来它是在自行车运输机倾斜后开始运行的。这是代码和输出

    静态循环载流子屏障;

        public static void main(String[] args) throws InterruptedException {
            barrier = new CyclicBarrier(3, new Runnable() {
                @Override
                public void run() {
                    System.out.println("I run in the beginning and after the CyclicBarrier is tipped");
                }
            });
    
            new Worker().start();
            Thread.sleep(1000);
            new Worker().start();
            Thread.sleep(1000);
            new Worker().start();
            Thread.sleep(1000);
    
            System.out.println("Barrier automatically resets.");
    
            new Worker().start();
            Thread.sleep(1000);
            new Worker().start();
            Thread.sleep(1000);
            new Worker().start();
        }
    

    输出

    I run in the beginning and after the CyclicBarrier is tipped
    Let's play.
    Let's play.
    Let's play.
    Barrier automatically resets.
    I run in the beginning and after the CyclicBarrier is tipped
    Let's play.
    Let's play.
    Let's play.
    
    推荐文章