Java — CyclicBarrier vs CountDownLatch

Sunaina Goyal
3 min readMar 24, 2021

When it comes to concurrency, both CountDownLatch and CyclicBarrier are used for managing multi-threaded applications.

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

CountDownLatch

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

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

CyclicBarrier

A 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.

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.

STATING THE DIFFERENCES

1. Tasks vs. Threads

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.

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

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

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.

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

CyclicBarrier, though, is different on this point.

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 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.

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!

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.

2. Reusability

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.

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

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.

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

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.

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.

--

--