CountDownLatch
is a synchronization aid that allows one or more threads to wait until a set of operations being performed by other threads is completed. It is particularly useful when you have a scenario where multiple threads need to complete certain tasks before another thread proceeds.
Key Use Cases for CountDownLatch
:
- Waiting for multiple threads to complete: A common use case is when the main thread or another coordinating thread must wait for several worker threads to finish their tasks before proceeding.
- Starting multiple threads at the same time:
CountDownLatch
can be used to ensure that multiple threads start simultaneously after some preparation work is done. - Dividing tasks: You can divide a task into multiple parts, and after all parts are processed by different threads, a final task can be executed.
How CountDownLatch
Works:
- CountDownLatch(int count): The latch is initialized with a count. This count is decremented by one every time a worker thread calls
countDown()
. await()
: A thread callingawait()
will be blocked until the count reaches zero (i.e., all worker threads have completed their work).countDown()
: Worker threads call this method when they finish their work, reducing the count by 1.
Example 1: Waiting for Multiple Threads to Complete
In this example, the main thread waits for three worker threads to finish their tasks.
import java.util.concurrent.CountDownLatch;
class Worker extends Thread {
private final CountDownLatch latch;
public Worker(String name, CountDownLatch latch) {
super(name);
this.latch = latch;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is doing some work...");
try {
Thread.sleep(2000); // Simulate work by sleeping
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " has finished.");
// Decrease the latch count by 1
latch.countDown();
}
}
public class Main {
public static void main(String[] args) {
// Create a CountDownLatch with a count of 3 (because we have 3 workers)
CountDownLatch latch = new CountDownLatch(3);
// Start three worker threads
Worker worker1 = new Worker("Worker-1", latch);
Worker worker2 = new Worker("Worker-2", latch);
Worker worker3 = new Worker("Worker-3", latch);
worker1.start();
worker2.start();
worker3.start();
// The main thread will wait until the latch count reaches 0
System.out.println("Main thread is waiting for workers to complete...");
try {
latch.await(); // Block until the count reaches 0
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All workers have finished. Main thread resumes.");
}
}
Explanation:
CountDownLatch(3)
: The latch is initialized with a count of 3. This means the main thread will wait until 3 worker threads callcountDown()
.latch.await()
: The main thread will wait here until the count reaches zero.latch.countDown()
: Each worker thread callscountDown()
after completing its work, reducing the latch count.- Once all worker threads have called
countDown()
, the latch count becomes 0, and the main thread resumes.
Output:
Main thread is waiting for workers to complete...
Worker-1 is doing some work...
Worker-2 is doing some work...
Worker-3 is doing some work...
Worker-1 has finished.
Worker-2 has finished.
Worker-3 has finished.
All workers have finished. Main thread resumes.
Example 2: Ensuring Multiple Threads Start Simultaneously
You can use CountDownLatch
to make sure that several threads start working at the same time after they are all ready.
import java.util.concurrent.CountDownLatch;
class WorkerTask extends Thread {
private final CountDownLatch startSignal;
public WorkerTask(String name, CountDownLatch startSignal) {
super(name);
this.startSignal = startSignal;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " is ready to start...");
startSignal.await(); // Wait until the main thread triggers the latch
System.out.println(Thread.currentThread().getName() + " is working...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
// Create a CountDownLatch with a count of 1 (to release all threads at once)
CountDownLatch startSignal = new CountDownLatch(1);
// Create and start worker threads
WorkerTask worker1 = new WorkerTask("Worker-1", startSignal);
WorkerTask worker2 = new WorkerTask("Worker-2", startSignal);
WorkerTask worker3 = new WorkerTask("Worker-3", startSignal);
worker1.start();
worker2.start();
worker3.start();
// Main thread does some work, then releases all workers at the same time
System.out.println("Main thread preparing to start workers...");
Thread.sleep(2000); // Simulate preparation time
System.out.println("Main thread signals all workers to start.");
startSignal.countDown(); // Release all workers at the same time
}
}
Explanation:
CountDownLatch(1)
: The latch starts with a count of 1, which is used to signal all worker threads simultaneously.startSignal.await()
: Each worker thread waits for the main thread to signal (reduce the latch count to 0).startSignal.countDown()
: The main thread calls this to release all worker threads at once, after its preparation is done.
Output:
Worker-1 is ready to start...
Worker-2 is ready to start...
Worker-3 is ready to start...
Main thread preparing to start workers...
Main thread signals all workers to start.
Worker-1 is working...
Worker-2 is working...
Worker-3 is working...
Summary of CountDownLatch
Usage:
- Use
CountDownLatch
when you need one or more threads to wait until a set of operations in other threads completes. - It allows coordination between threads, ensuring proper synchronization where certain tasks cannot proceed until others are finished.
- Common scenarios include:
- Waiting for multiple threads to complete before proceeding.
- Starting multiple threads simultaneously after an initial setup.
In both examples, CountDownLatch
ensures that threads either start simultaneously or wait for others to finish before continuing, making it a useful tool in multithreading scenarios where timing and synchronization are important.