【问题标题】:How does CountDownLatch works in Java? [duplicate]CountDownLatch 在 Java 中是如何工作的? [复制]
【发布时间】:2015-02-04 05:16:33
【问题描述】:

我正在学习 Java 中的同步。我无法理解 CountDownLatch 的确切机制。

CountDownLatch '倒计时'(等待线程数的完成)是否根据声明时给出的线程数?

这是我试图理解的代码:

public class LatchExample implements Runnable {
    private CountDownLatch latch;

    private int id;

    public LatchExample(int id, CountDownLatch latch){
        this.id=id;
        this.latch = latch;
    }

    public static void main(String[] args) {

        CountDownLatch latch = new CountDownLatch(5);

        ExecutorService executor = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 7; i++) {
            executor.submit(new LatchExample(i,latch));
        }

        try {
            latch.await();
            System.out.println("all process completed");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        System.out.println();
    }

    @Override
    public void run() {

        System.out.println("Starting: "+id);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        latch.countDown();
    }
}

在上面的例子中:

7 个线程由 ExecutorService(来自线程池)产生。我的理解是,latch 应该等待 6 个线程(从 0 到 5)完成,定义为:

CountDownLatch latch = new CountDownLatch(5);

但是我得到的输出并不是每次都保持不变。有时它等待 6 个线程完成,有时它等待 7 个,例如:

Starting: 1
Starting: 0
Starting: 2
Starting: 3
Starting: 5
Starting: 4
Starting: 6
all process completed

这里是交替输出:

Starting: 0
Starting: 2
Starting: 1
Starting: 4
Starting: 5
Starting: 3
all process completed

Starting: 6

编辑:理想情况下,CountDownLatch 应该 countDown 直到 5 个任务通过锁存器。这里显示为 6 或 7。

如果我希望它在 'all process completed' 之前始终只显示 5 个任务,那么代码的修复方法是什么?

【问题讨论】:

  • 你为什么给CountDownLatch 5而不是7?在 CountDownLatch 的 java 文档中的示例中,查看 N
  • @Jack 如果我只想等待 5 或 6 个线程的完成?这就是为什么。
  • 好吧,那么它工作正常。这只是一种竞争条件,因为一次可以运行多个线程...Executors.newFixedThreadPool(3);
  • 您的执行程序池不会产生 7 个线程,而是产生 3 个线程和 7 个任务。而在“完成”之前打印“开始”这一事实并不意味着它实际上正在等待它。
  • @Jack 它工作不正常。理想情况下,它应该只为 5 个任务倒计时。它适用于 6 个或 7 个任务。

标签: java multithreading countdownlatch


【解决方案1】:

CountDownLatch 用数字初始化,这个数字表示 countDown() 必须被调用多少次,等待 await() 的线程才能触发。

new CountDownLatch(5) 

countDown() 必须被调用 5 次,然后等待 await() 的线程才能被触发。 所以把你的代码改成

new CountDownLatch(7) 

【讨论】:

    【解决方案2】:

    它独立于线程。

    当有人在倒计时锁存器上调用 await 时,如果计数器不为零,他们将阻塞。他们将继续阻塞,直到计数器达到零,或者直到他们达到超时(如果他们调用了允许你超时的等待版本)。

    你可以让一个线程做工作,递减计数器,另一个线程等待这 n 件事完成。您还可以有一个值为 1 的倒计时锁存器,并让另一个线程在构建 gui 时进行初始化。然后在使gui可见之前将主线程块放在闩锁上。有很多可能性。

    【讨论】:

      【解决方案3】:

      您的闩锁需要五次countDown() 调用才能达到零并让await 返回。但是,按照执行器任务的编写方式,开始运行的任务将多于释放闩锁所需的任务。查看代码所在位置的最有用方法是逐步运行,如下所示:

      1. 任务 0-2 开始在三个线程池线程上运行;
      2. 您会看到Starting: n 打印了三次,其中n 的范围在 0 和 2 之间,但顺序是任意的;
      3. 第二次传球;
      4. 三个任务几乎同时完成,使锁存器的计数减少到 2;
      5. 任务 3-5 开始运行;
      6. 您会看到Starting: n 打印了 3 次,n 介于 3 和 5 之间,顺序任意;
      7. 又是第二次传球;
      8. 任务 3-5 几乎同时完成,释放闩锁。现在主线程可以继续,任务 6 可以启动;
      9. all processes completed 几乎与 Starting: 6 以任意顺序打印。

      现在,我不太清楚您期望您的代码会做什么,但我希望上述推理方式能帮助您将其行为带到符合您期望的状态。

      【讨论】:

        【解决方案4】:

        我认为理解 countDownLatch 背后机制的最好方法是进行类比,所以我的 2 美分是这样的:想象一个迪斯科派对,你是 DJ,你不会放你最喜欢的名为 的歌曲。”所有过程都已完成”,直到您数出舞池中有 5 人。每次有人进来时,您都会倒计时(从 5 开始),当您到达 0 时 - 您会放上您最喜欢的歌曲。你不在乎他们是否都在舞池里,你只在乎你在舞池里数了5个人。

        现在,对于您的示例,您输入 countDownLatch=5 并且您的 for 循环具有

        你问:

        CountDownLatch 是否“倒计时”(等待完成 线程数)根据给出的线程数 声明?

        不,它不等待线程完成,它根本不关心线程,它只关心你的计数。

        在现实世界中,当您想要确保所有线程都已执行特定任务并且现在您已准备好继续执行 main(或其他线程)时,您将使用此机制。

        【讨论】:

          【解决方案5】:

          CountDownLatch 允许线程在您 countDown 闩锁中所有可用的许可后通过。

          在这种情况下,由于您有 5 个许可,但有 7 个线程,第 5 个线程将解锁闩锁,程序继续。但是,您还有 2 个线程。

          自从锁存器被解锁后,程序在没有最后 2 个线程的情况下继续运行,因为没有许可留给countDown。要解决此问题,只需 7 个许可证而不是 5 个。

          【讨论】:

            【解决方案6】:

            尽管我的回答为时已晚,但希望这对像我这样的线程新手有所帮助。根据我的分析,倒计时锁存器按预期工作。请进行此更改并查看输出

             @Override
             public void run() {
            
                //System.out.println("Starting: "+id);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            
                latch.countDown();
                System.out.println("Finished the thread: "+id);
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-02-25
              • 2013-11-03
              • 1970-01-01
              • 1970-01-01
              • 2014-09-23
              • 2016-02-20
              • 1970-01-01
              相关资源
              最近更新 更多