Understanding Java CyclicBarrier: Usage, Implementation Details, and Comparison with CountDownLatch

This article explains the concept, practical usage, internal implementation, and key methods of Java's CyclicBarrier, compares it with CountDownLatch, and provides a complete code example demonstrating how multiple threads can repeatedly synchronize at a reusable barrier.

Full-Stack Internet Architecture
Full-Stack Internet Architecture
Full-Stack Internet Architecture
Understanding Java CyclicBarrier: Usage, Implementation Details, and Comparison with CountDownLatch

After introducing CountDownLatch in a previous article, the author points out that CountDownLatch is a one‑time counter and therefore unsuitable for scenarios that require repeated synchronization, such as multiple rounds of a game; the article then introduces CyclicBarrier as a reusable barrier.

CyclicBarrier (literally “cyclic barrier”) provides a reusable synchronization point where a group of threads wait until a predefined number of parties have arrived, after which all waiting threads are released and the barrier can be used again.

The author uses a bus‑loading analogy: a bus departs only when it is full, illustrating how CyclicBarrier fits scenarios where a group must wait until enough participants are present before proceeding.

A side‑by‑side comparison shows that CountDownLatch works like a game loading screen—threads wait for a single event—whereas CyclicBarrier works like a driver waiting for all seats to be filled before the trip starts, emphasizing its repeatable nature.

import java.util.Date;
import java.util.Random;
import java.util.concurrent.*;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        final CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
            @Override
            public void run() {
                System.out.println("People are full, ready to depart: " + new Date());
            }
        });
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                int randomNumber = new Random().nextInt(3) + 1;
                System.out.println(String.format("I am %s, %d seconds to station, now: %s",
                        Thread.currentThread().getName(), randomNumber, new Date()));
                try {
                    TimeUnit.SECONDS.sleep(randomNumber);
                    cyclicBarrier.await();
                    System.out.println(String.format("Thread %s boarded, time: %s",
                            Thread.currentThread().getName(), new Date()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        };
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        threadPool.submit(runnable);
        threadPool.submit(runnable);
        threadPool.submit(runnable);
        threadPool.submit(runnable);
        threadPool.shutdown();
    }
}

The execution result demonstrates that when the barrier count is set to 2, the first two threads reach the barrier and trigger the barrier action, then after a short delay the next two threads reach the barrier and trigger the action again, showing the reusable nature of CyclicBarrier.

Internally, CyclicBarrier is built on a ReentrantLock (which itself relies on AQS). It maintains an internal counter that decrements each time a thread calls await(). When the counter reaches zero, all waiting threads are released and the counter is reset to the original parties value for the next round.

Key methods include:

CyclicBarrier(int parties) : creates a barrier for the given number of parties.

CyclicBarrier(int parties, Runnable barrierAction) : also specifies an action to run when the barrier is tripped.

getParties() : returns the number of parties required to trip the barrier.

getNumberWaiting() : returns the number of threads currently waiting.

await() and await(long timeout, TimeUnit unit) : block until the barrier is tripped or an exception occurs (InterruptedException, BrokenBarrierException, TimeoutException).

isBroken() : indicates whether the barrier is in a broken state.

reset() : returns the barrier to its initial state, releasing waiting threads with a BrokenBarrierException and clearing the broken flag.

In summary, CyclicBarrier uses a ReentrantLock to ensure atomic updates of its counter, and its primary advantage over CountDownLatch is the ability to be reused across multiple synchronization cycles, making it a valuable tool for complex multithreaded coordination.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javabackend-developmentconcurrencyCountDownLatchCyclicBarrier
Full-Stack Internet Architecture
Written by

Full-Stack Internet Architecture

Introducing full-stack Internet architecture technologies centered on Java

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.