Java CyclicBarrier vs CountDownLatch – Java CyclicBarrier vs CountDownLatch

最后修改: 2018年 11月 28日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this tutorial, we’ll compare CyclicBarrier and CountDownLatch and try to understand the similarities and differences between the two.

在本教程中,我们将比较CyclicBarrierCountDownLatch,并尝试了解两者的异同。

2. What Are These?

2.这些是什么?

When it comes to concurrency, it can be challenging to conceptualize what each is intended to accomplish.

当涉及到并发时,将每个人要完成的任务概念化可能是一个挑战。

First and foremost, both CountDownLatch and CyclicBarrier are used for managing multi-threaded applications.

首先,CountDownLatchCyclicBarrier都是用于管理多线程的应用程序

And, they are both intended to express how a given thread or group of threads should wait.

而且,它们都是为了表达一个特定的线程或线程组应该如何等待。

2.1. CountDownLatch

2.1.CountDownLatch[/em

CountDownLatch is a construct that a thread waits on while other threads count down on the latch until it reaches zero.

一个CountDownLatch是一个构造,一个线程等待,而其他线程倒数在锁存器上,直到它达到零。

We can think of this like a dish at a restaurant that is being prepared. No matter which cook prepares however many of the items, the waiter must wait until all the items are on the plate. If a plate takes items, any cook will count down on the latch for each item she puts on the plate.

我们可以把这想象成餐厅里正在准备的一道菜。无论哪个厨师准备了多少个n 项目,服务员都必须等待,直到所有项目都在盘子里。如果一个盘子需要n 个项目,任何一个厨师都会在闩子上往下数她放在盘子上的每个项目。

2.2. CyclicBarrier

2.2.CyclicBarrier</em

CyclicBarrier is a reusable construct where a group of threads waits together until all of the threads arrive. At that point, the barrier is broken and an action can optionally be taken.

CyclicBarrier是一种可重复使用的构造,一组线程一起等待,直到所有线程到达。在这一点上,屏障被打破,可以选择采取行动

We can think of this like a group of friends. Every time they plan to eat at a restaurant they decide a common point where they can meet. They wait for each other there, and only when everyone arrives can they go to the restaurant to eat together.

我们可以把这想象成一群朋友。每次他们计划在一家餐厅吃饭时,都会决定一个共同点,在那里见面。他们在那里等待对方,只有当每个人都到达时,他们才能去餐厅一起吃饭。

2.3. Further Reading

2.3.进一步阅读

And for a lot more detail on each of these individually, refer to our previous tutorials on CountDownLatch and CyclicBarrier respectively.

而关于其中每一项的更多细节,请参考我们之前的CountDownLatchCyclicBarrier的教程。

3. Tasks vs. Threads

3.任务与线程

Let’s take a deeper dive into some of the semantic differences between these two classes.

让我们深入了解一下这两个类之间的一些语义差异。

As stated in the definitions, CyclicBarrier allows a number of threads to wait on each other, whereas CountDownLatch allows one or more threads to wait for a number of tasks to complete.

正如定义中所说,CyclicBarrier允许若干线程相互等待,而CountDownLatch允许一个或多个线程等待若干任务的完成。

In short, CyclicBarrier maintains a count of threads whereas CountDownLatch maintains a count of tasks.

简而言之,CyclicBarrier维护线程的计数,而CountDownLatch维护任务的计数。

In the following code, we define a CountDownLatch with a count of two. Next, we call countDown() twice from a single thread:

在下面的代码中,我们定义了一个CountDownLatch,计数为2。接下来,我们从一个线程中调用countDown()两次。

CountDownLatch countDownLatch = new CountDownLatch(2);
Thread t = new Thread(() -> {
    countDownLatch.countDown();
    countDownLatch.countDown();
});
t.start();
countDownLatch.await();

assertEquals(0, countDownLatch.getCount());

Once the latch reaches zero, the call to await returns.

一旦锁存器达到零,对await的调用就会返回。

Note that in this case, we were able to have the same thread decrease the count twice.

请注意,在这种情况下,我们能够让同一个线程减少两次计数。

CyclicBarrier, though, is different on this point.

CyclicBarrier,不过,在这一点上是不同的。

Similar to the above example, we create a CyclicBarrier, again with a count of two and call await() on it, this time from the same thread:

与上面的例子类似,我们再次创建一个CyclicBarrier,计数为2,并对其调用await(),这次是在同一个线程。

CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
Thread t = new Thread(() -> {
    try {
        cyclicBarrier.await();
        cyclicBarrier.await();    
    } catch (InterruptedException | BrokenBarrierException e) {
        // error handling
    }
});
t.start();

assertEquals(1, cyclicBarrier.getNumberWaiting());
assertFalse(cyclicBarrier.isBroken());

The first difference here is that the threads that are waiting are themselves the barrier.

这里的第一个区别是,正在等待的线程本身就是障碍。

Second, and more importantly, the second await() is useless. A single thread can’t count down a barrier twice.

第二,更重要的是,第二个await()是无用的一个线程不可能倒数一个障碍物两次。

Indeed, because t must wait for another thread to call await() – to bring the count to two – t‘s second call to await() won’t actually be invoked until the barrier is already broken!

事实上,因为t必须等待另一个线程调用await()–使计数达到2–tawait()的第二次调用实际上不会被调用,直到障碍已经被打破。

In our test, the barrier hasn’t been crossed because we only have one thread waiting and not the two threads that would be required for the barrier to be tripped. This is also evident from the cyclicBarrier.isBroken() method, which returns false.

在我们的测试中,屏障还没有被跨越,因为我们只有一个线程在等待,而不是屏障被触发所需的两个线程。这一点从cyclicBarrier.isBroken()方法中也可以看出,它返回false

4. Reusability

4.可重复使用

The second most evident difference between these two classes is reusability. To elaborate, when the barrier trips in CyclicBarrier, the count resets to its original value. CountDownLatch is different because the count never resets.

这两个类之间第二个最明显的区别是可重用性。详细来说,CyclicBarrier中的障碍物跳闸时,计数会重置为原来的值。 CountDownLatch是不同的,因为计数从未重置。

In the given code, we define a CountDownLatch with count 7 and count it through 20 different calls:

在给定的代码中,我们定义了一个计数为7的CountDownLatch,并通过20个不同的调用进行计数。

CountDownLatch countDownLatch = new CountDownLatch(7);
ExecutorService es = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
    es.execute(() -> {
        long prevValue = countDownLatch.getCount();
        countDownLatch.countDown();
        if (countDownLatch.getCount() != prevValue) {
            outputScraper.add("Count Updated");
        }
    }); 
} 
es.shutdown();

assertTrue(outputScraper.size() <= 7);

We observe that even though 20 different threads call countDown(), the count doesn’t reset once it reaches zero.

我们观察到,即使有20个不同的线程调用countDown(),一旦达到零,计数也不会重置。

Similar to the above example, we define a CyclicBarrier with count 7 and wait on it from 20 different threads:

与上面的例子类似,我们定义了一个计数为7的CyclicBarrier,并从20个不同的线程中等待它。

CyclicBarrier cyclicBarrier = new CyclicBarrier(7);

ExecutorService es = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
    es.execute(() -> {
        try {
            if (cyclicBarrier.getNumberWaiting() <= 0) {
                outputScraper.add("Count Updated");
            }
            cyclicBarrier.await();
        } catch (InterruptedException | BrokenBarrierException e) {
            // error handling
        }
    });
}
es.shutdown();

assertTrue(outputScraper.size() > 7);

In this case, we observe that the value decreases every time a new thread runs, by resetting to the original value, once it reaches zero.

在这种情况下,我们观察到,每当一个新的线程运行时,该值就会减少,一旦达到零,就会重设为原始值。

5. Conclusion

5.总结

All in all, CyclicBarrier and CountDownLatch are both helpful tools for synchronization between multiple threads. However, they are fundamentally different in terms of the functionality they provide. Consider each carefully when determining which is right for the job.

总而言之,CyclicBarrierCountDownLatch都是在多个线程之间进行同步的有用工具。然而,它们在提供的功能方面有根本的不同。在确定哪一个适合工作时,要仔细考虑每一个。

As usual, all the discussed examples can be accessed over on Github.

像往常一样,所有讨论的例子都可以在Github上访问