Phaser
and CountDownLatch
. CountDownLatch
is a simple yet useful synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes. On the other hand, Phaser
is a more advanced and flexible synchronization barrier that can be used to coordinate multiple phases of work among multiple threads. This blog will explore these two concepts in detail, including their fundamental concepts, usage methods, common practices, and best practices.A CountDownLatch
is initialized with a given count. The await()
method blocks the calling thread until the internal count reaches zero. Other threads can decrement the count by calling the countDown()
method. Once the count reaches zero, all threads waiting on the await()
method are released, and subsequent calls to await()
return immediately.
A Phaser
represents a reusable synchronization barrier, similar to a CyclicBarrier
and a CountDownLatch
but supporting more flexible usage. A Phaser
can be used to coordinate multiple phases of work. Threads can register themselves with a Phaser
, and they can wait for all registered threads to reach a particular phase. When all threads have arrived at a phase, the Phaser
advances to the next phase.
The following is a simple example of using CountDownLatch
to wait for multiple threads to complete their tasks:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int threadCount = 3;
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " is working.");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}).start();
}
latch.await();
System.out.println("All threads have completed their tasks.");
}
}
In this example, we create a CountDownLatch
with an initial count of 3. Three threads are started, and each thread simulates some work by sleeping for 1 second. After the work is done, each thread calls latch.countDown()
. The main thread calls latch.await()
and waits until all three threads have called countDown()
.
The following is an example of using Phaser
to coordinate multiple phases of work:
import java.util.concurrent.Phaser;
public class PhaserExample {
public static void main(String[] args) {
int threadCount = 3;
Phaser phaser = new Phaser(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
for (int phase = 0; phase < 2; phase++) {
System.out.println(Thread.currentThread().getName() + " is working on phase " + phase);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
phaser.arriveAndAwaitAdvance();
}
}).start();
}
}
}
In this example, we create a Phaser
with an initial registration of 3 threads. Each thread performs two phases of work. After completing each phase, the thread calls phaser.arriveAndAwaitAdvance()
, which indicates that it has arrived at the current phase and waits for all other registered threads to arrive at the same phase before proceeding to the next phase.
CountDownLatch
with the number of tasks that need to be completed.countDown()
when it is completed.await()
in the main thread or any thread that needs to wait for all tasks to finish.Phaser
during initialization.arriveAndAwaitAdvance()
after completing each phase.register()
and arriveAndDeregister()
methods to dynamically manage the number of registered threads.CountDownLatch
is a one - time use object. Once the count reaches zero, it cannot be reset. If you need a reusable synchronization mechanism, consider using other alternatives like CyclicBarrier
.countDown()
in a finally
block to ensure that the count is decremented even if an exception occurs in the task.Both CountDownLatch
and Phaser
are valuable tools for thread coordination in Java. CountDownLatch
is suitable for simple scenarios where one or more threads need to wait for a fixed number of tasks to complete. Phaser
, on the other hand, is more flexible and can handle multiple phases of work among multiple threads. By understanding their fundamental concepts, usage methods, common practices, and best practices, developers can effectively use these synchronization mechanisms to build robust multi - threaded applications.