1

I am studying Synchronization in Java. I am not able to understand the exact mechanism of CountDownLatch.

Does CountDownLatch 'counts down the latch' (waits for completion of number of threads) as per the number of threads which are given at declaration?

Here is the code I tried to understand:

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();
    }
}

In the example above:

7 threads are spawned by ExecutorService (from the Thread pool). My understanding is that the latch should wait for completion of 6 threads (from 0 to 5), as defined by:

CountDownLatch latch = new CountDownLatch(5);

But the output that I get is not constant every time. Sometimes it waits for 6 threads to complete and sometimes it waits for 7 e.g.:

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

Here is output at alternate times:

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

Starting: 6

EDIT : The CountDownLatch should ideally countDown until 5 tasks are passed the latch. Here it is showing as either 6 or 7.

What would be the fix for the code, if I want it to always display only 5 tasks before 'all process completed' ?

4

6 回答 6

4

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

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

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

于 2014-12-05T23:14:05.357 回答
2

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

现在,对于您的示例,您输入 countDownLatch=5 并且您的 for 循环具有 <7 (没关系)。所以当 5 位舞者进来时——你放了歌曲“所有过程完成”;所以结果还可以。

您问:

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

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

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

于 2014-12-06T02:19:16.080 回答
1

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

 @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);
}
于 2016-08-01T14:24:10.210 回答
0

ACountDownLatch允许线程在您countDown获得所有可用的许可后通过锁存器。

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

由于闩锁已解锁,因此程序在没有最后 2 个线程的情况下继续运行,因为没有剩余的许可countDown。要解决此问题,只需 7 个许可证而不是 5 个。

于 2014-12-06T04:28:51.330 回答
0

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

new CountDownLatch(5) 

countDown() 必须被调用 5 次,然后等待 await() 的线程才能被触发。因此,将您的代码更改为

new CountDownLatch(7) 
于 2014-12-05T23:12:40.183 回答
0

它独立于线程。

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

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

于 2014-12-05T23:13:47.717 回答