54 Differences Between Cyclic Barrier and Countdown Latch

54 Differences Between CyclicBarrier and CountdownLatch #

In this lesson, we will mainly discuss the differences between CyclicBarrier and CountdownLatch.

CyclicBarrier #

Purpose #

CyclicBarrier and CountdownLatch do have some similarities. They both can block one or a group of threads until a certain predefined condition is met. Only when the waiting threads reach the specified condition, they will be released and continue executing. Due to this similarity, you may think that their purposes are exactly the same, but they are actually different.

CyclicBarrier can construct a rendezvous point. When a thread calls await(), it will start waiting at this rendezvous point until the barrier is broken. Once the specified number of threads reach this rendezvous point, the barrier will be broken, and the waiting threads will continue executing and proceed to the remaining tasks.

Let’s take an example from daily life. Suppose our class goes on a spring outing to a park and there are three bikes to be rented. Each person can ride a bike, but since each bike is for three people, three people need to gather before they can ride together. Also, it takes some time to walk from the park gate to the bike rental station. Let’s simulate this scenario with the following code:

public class CyclicBarrierDemo {

    public static void main(String[] args) {

        CyclicBarrier cyclicBarrier = new CyclicBarrier(3);

        for (int i = 0; i < 6; i++) {

            new Thread(new Task(i + 1, cyclicBarrier)).start();

        }

    }

    static class Task implements Runnable {

        private int id;

        private CyclicBarrier cyclicBarrier;

        public Task(int id, CyclicBarrier cyclicBarrier) {

            this.id = id;

            this.cyclicBarrier = cyclicBarrier;

        }

        @Override

        public void run() {

            System.out.println("Student " + id + " is now leaving the gate and heading to the bike station");

            try {

                Thread.sleep((long) (Math.random() * 10000));

                System.out.println("Student " + id + " has arrived at the bike station and is waiting for others");

                cyclicBarrier.await();

                System.out.println("Student " + id + " starts riding the bike");

            } catch (InterruptedException e) {

                e.printStackTrace();

            } catch (BrokenBarrierException e) {

                e.printStackTrace();

            }

        }

    }

}

In this code, you can see that we first create a CyclicBarrier with a parameter of 3. This means that we need to wait for 3 threads to reach this rendezvous point before they are released. Then, we start 6 threads in the for loop, and each thread executes the Runnable object defined in the Task class below. Take a look at its run method. It first prints “Student [id] is now leaving the gate and heading to the bike station”. Then, it sleeps for a random amount of time, which represents the time it takes to walk from the gate to the bike station. Since each student has a different walking speed, we use a random value to simulate it.

When all the students have reached the bike station, for example, when one student reaches the station, it first prints “Student [id] has arrived at the bike station and is waiting for others” and then calls the await() method of CyclicBarrier. Once it calls this method, it will enter a waiting state until three people gather. Once three people gather, the thread will continue executing. When it does, it means that the three students start riding the bike together, so it prints the statement “Student [id] starts riding the bike”.

Let’s run this program and see the output:

Student 1 is now leaving the gate and heading to the bike station
Student 3 is now leaving the gate and heading to the bike station
Student 2 is now leaving the gate and heading to the bike station
Student 4 is now leaving the gate and heading to the bike station
Student 5 is now leaving the gate and heading to the bike station
Student 6 is now leaving the gate and heading to the bike station
Student 5 has arrived at the bike station and is waiting for others
Student 2 has arrived at the bike station and is waiting for others
Student 3 has arrived at the bike station and is waiting for others
Student 3 starts riding the bike
Student 5 starts riding the bike
Student 2 starts riding the bike

As you can see from the output, the students first leave the gate and head to the bike station. Then, when some students reach the station, they wait for others to arrive. Once the required number of students have arrived, they start riding the bikes together.

Students at the bicycle station #

Student 6 arrived at the bicycle station and started waiting for others to arrive.

Student 4 arrived at the bicycle station and started waiting for others to arrive.

Student 1 arrived at the bicycle station and started waiting for others to arrive.

Student 1 started riding the bike.

Student 6 started riding the bike.

Student 4 started riding the bike.

We can see that 6 students walked from the entrance to the bicycle station. Because everyone walks at different speeds, there will be 3 students who arrive at the bicycle station first. However, among these 3 students, the first 2 must wait for the third person to arrive before they can start riding the bike. The same goes for the rest of the students. Since the first bike has already been taken, the second bike should also wait for 3 people to gather before they can start together.

If you want to achieve this without using CyclicBarrier, the logic may be very complex because you don’t know which student arrives first and which arrives later. However, by using CyclicBarrier, you can implement this logic very simply and elegantly, which is a very typical use case of CyclicBarrier.

Executing barrierAction #

public CyclicBarrier(int parties, Runnable barrierAction): When parties number of threads reach the rendezvous point and continue to execute, this action will be executed once.

Next, let’s introduce an additional feature of CyclicBarrier, which is the execution of the barrierAction function. CyclicBarrier also has a constructor that takes two parameters. The first parameter is still parties, which represents how many threads need to gather. The second parameter is a Runnable object, which is the barrierAction we are going to introduce below.

When the predetermined number of threads reach the rendezvous point, the Runnable object passed in here will be executed before starting. Let’s change the constructor of the previous code to the following:

CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {

    @Override
    
    public void run() {

        System.out.println("3 people gathered, let's go!");

    }

});

As you can see, we pass in the second parameter, which is a Runnable object. After passing in this Runnable, this task will print “3 people gathered, let’s go!” when it reaches the rendezvous point. If the above code is changed to this, the execution result is as follows:

Student 1 starts from the entrance and goes to the bicycle station.

Student 3 starts from the entrance and goes to the bicycle station.

Student 2 starts from the entrance and goes to the bicycle station.

Student 4 starts from the entrance and goes to the bicycle station.

Student 5 starts from the entrance and goes to the bicycle station.

Student 6 starts from the entrance and goes to the bicycle station.

Student 2 arrives at the bicycle station and starts waiting for others to arrive.

Student 4 arrives at the bicycle station and starts waiting for others to arrive.

Student 6 arrives at the bicycle station and starts waiting for others to arrive.

3 people gathered, let's go!

Student 6 starts riding the bike.

Student 2 starts riding the bike.

Student 4 starts riding the bike.

Student 1 arrives at the bicycle station and starts waiting for others to arrive.

Student 3 arrives at the bicycle station and starts waiting for others to arrive.

Student 5 arrives at the bicycle station and starts waiting for others to arrive.

3 people gathered, let's go!

Student 5 starts riding the bike.

Student 1 starts riding the bike.

Student 3 starts riding the bike.

As you can see, after three people gather together, the statement “3 people gathered, let’s go!” is printed. This statement is exactly the result of the execution of the Runnable passed in here.

It is worth noting that this statement is printed only once per cycle, not once for each thread waiting. It means that this task is executed only once when the “gate” is opened.

Similarities and differences between CyclicBarrier and CountDownLatch #

Now let’s summarize the similarities and differences between CyclicBarrier and CountDownLatch.

Similarities: Both can block one or a group of threads until a predetermined condition occurs before proceeding together.

However, they also have many differences, as follows.

  • Target of action: CyclicBarrier needs a fixed number of threads to reach the barrier position before it can continue executing, while CountDownLatch only needs to wait for the number to count down to 0. This means that CountDownLatch works with events, while CyclicBarrier works with threads. CountDownLatch reduces the count by calling the countDown method, while CyclicBarrier reduces the count when a thread begins waiting by calling the await method.
  • Reusability: Once CountDownLatch counts down to 0 and triggers the latch to open, it cannot be used again unless a new instance is created. CyclicBarrier can be reused. As you can see in the previous code, each group of 3 students can start after they arrive without creating a new instance. CyclicBarrier can also be reset at any time by calling the reset method. If any threads have already called the await method and started waiting, these threads will throw a BrokenBarrierException exception.
  • Execution action: CyclicBarrier has the barrierAction execution action, while CountDownLatch does not have this feature.

Summary #

The above is the content of this lesson. In this lesson, we first introduced the use, code examples, and execution actions of CyclicBarrier. Then, we summarized the similarities and differences between CyclicBarrier and CountDownLatch.