Concurrency is an integral part of modern software development, especially in applications that require parallel processing and efficient utilization of system resources. Java, being a widely used programming language, provides several built-in mechanisms to support concurrent programming. Two of the most commonly used synchronization mechanisms in Java are CountDownLatch
and CyclicBarrier
. In this article, we will explore the concepts and usage of these two synchronization constructs.
CountDownLatch
is a synchronization primitive that allows one or more threads to wait until a set of operations being performed in other threads completes. It works by creating a latch with an initial count, and each thread that needs to wait for the completion of these operations calls the await()
method on the latch. The await()
method blocks the calling thread until the count reaches zero.
Let's consider an example where we have a program that needs to gather information from multiple external APIs before proceeding to the next step. We can use CountDownLatch
to ensure that the program waits until all API calls complete and data is gathered before proceeding. Here's how we can achieve this:
import java.util.concurrent.CountDownLatch;
public class DataGatherer {
public static void main(String[] args) throws InterruptedException {
int numberOfAPIs = 3;
CountDownLatch latch = new CountDownLatch(numberOfAPIs);
// Start threads to make API calls
for (int i = 0; i < numberOfAPIs; i++) {
Thread thread = new Thread(new APIWorker(latch));
thread.start();
}
// Wait until all API calls complete
latch.await();
// All API calls completed, proceed to next step
System.out.println("Data gathered from all APIs. Proceeding to the next step.");
}
}
class APIWorker implements Runnable {
private final CountDownLatch latch;
public APIWorker(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
// Perform API call and gather data
// Signal to the latch that the operation is complete
latch.countDown();
}
}
In this example, we create a CountDownLatch
with an initial count of 3, indicating that we have 3 API calls to make before proceeding. Each thread created by the DataGatherer
class would pass the same latch instance to the APIWorker
class, which performs the API call and signals the latch by calling countDown()
method. The DataGatherer
class then waits for the latch count to reach zero using the await()
method. Once all API calls complete, the program can proceed to the next step.
CyclicBarrier
is another synchronization construct that allows a set of threads to wait for each other at a common point before proceeding. Unlike CountDownLatch
, CyclicBarrier
allows the waiting threads to perform certain actions before releasing them all at once. This construct is particularly useful in situations where parallel tasks need to coordinate and synchronize.
Let's consider an example where we have a scenario of a multiplayer game where all players need to gather at a specific point on the map before starting the game. We can use CyclicBarrier
to ensure that all players arrive at the designated location before the game begins. Here's how we can implement this using CyclicBarrier
:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class MultiplayerGame {
public static void main(String[] args) {
int numberOfPlayers = 4;
CyclicBarrier barrier = new CyclicBarrier(numberOfPlayers, () ->
System.out.println("All players gathered. Starting the game now!")
);
// Start threads for each player
for (int i = 0; i < numberOfPlayers; i++) {
Thread playerThread = new Thread(new Player(barrier));
playerThread.start();
}
}
}
class Player implements Runnable {
private final CyclicBarrier barrier;
public Player(CyclicBarrier barrier) {
this.barrier = barrier;
}
@Override
public void run() {
// Perform player-specific actions
try {
// Signal arrival at the gathering point
barrier.await();
// Perform game actions after all players have gathered
System.out.println("Player performing game actions.");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
In this example, we create a CyclicBarrier
with the number of players and a Runnable
action to be executed when all players reach the gathering point. Each player, represented by a separate thread, would pass the same barrier instance to the Player
class. The Player
class performs player-specific actions and then calls the await()
method on the barrier to signal its arrival at the gathering point. Once all players have arrived, the Runnable
action provided to the barrier is executed. Finally, each player proceeds to perform game actions.
CountDownLatch
and CyclicBarrier
are powerful synchronization constructs provided by Java that can simplify the coordination and synchronization of multiple threads. They allow us to control the flow of execution and ensure that certain operations happen in a coordinated manner. Understanding and effectively using these constructs can significantly enhance the performance and reliability of concurrent applications in Java.
noob to master © copyleft