CountDownLatch is synchronisation aid that allow one or more threads to wait until set of operations being performed in other threads completes.
So in other words, CountDownLatch waits for other threads to complete set of operations.
CountDownLatch is initialized with count. Any thread generally main threads calls latch.awaits() method, so it will wait for either count becomes zero or it’s interrupted by another thread and all other thread need to call latch.countDown() once they complete some operation.
So count is reduced by 1 whenever latch.countDown() method get called, so if count is n that means count can be used as n threads have to complete some action or some action have to be completed n times.
One of disadvantage of CountDownLatch is you can not reuse it once count is zero. For that ,you need to use CyclicBarrier.
For example:
Below diagram will make you clear. It is an example how CountDownLatch can be used.
Let’s say, you are developing an application, so it’s main thread has to wait for other services (threads) such as UI initialization, database initialization and logging services to get completed. So Countdownlatch will be initialized with 3 and main thread will call await() method and each services will call latch.countDown() once they are about to complete.
Program :
Create a class named UIInitialization.java. This thread will execute latch.countDown() once it completes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package org.arpit.java2blog.thread;
import java.util.concurrent.CountDownLatch;
public class UIInitialization implements Runnable{
CountDownLatch latch;
UIInitialization(CountDownLatch latch)
{
this.latch=latch;
}
@Override
public void run() {
System.out.println("Initializing UI");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Done with UI Initialization");
latch.countDown();
}
}
|
Create a class named LoggingInitialization.java. This thread will execute latch.countDown() once it completes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package org.arpit.java2blog.thread;
import java.util.concurrent.CountDownLatch;
public class LoggingInitialization implements Runnable{
CountDownLatch latch;
LoggingInitialization(CountDownLatch latch)
{
this.latch=latch;
}
@Override
public void run() {
System.out.println("Initializing Logging");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Done with Logging Initialization");
latch.countDown();
}
}
|
Create a class named DatabaseInitialization.java. This thread will execute latch.countDown() once it completes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package org.arpit.java2blog.thread;
import java.util.concurrent.CountDownLatch;
public class DatabaseInitialization implements Runnable{
CountDownLatch latch;
DatabaseInitialization(CountDownLatch latch)
{
this.latch=latch;
}
@Override
public void run() {
System.out.println("Initializing Database");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Done with database Initialization");
latch.countDown();
}
}
|
Create a class CountDownLatchMain.java. This will be main thread which will wait for UIInitialization, DatabaseInitialization and LoggingInitialization.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package org.arpit.java2blog.thread;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchMain {
public static void main(String[] args) {
try {
CountDownLatch latch = new CountDownLatch(3);
// Initializing three dependent thread i.e. UI, database and logging
UIInitialization uiInitialization = new UIInitialization(latch);
Thread uiThread = new Thread(uiInitialization);
DatabaseInitialization dataBaseInitialization = new DatabaseInitialization(latch);
Thread databaseThread = new Thread(dataBaseInitialization);
LoggingInitialization loggingInitialization = new LoggingInitialization(latch);
Thread loggingThread = new Thread(loggingInitialization);
uiThread.start();
databaseThread.start();
loggingThread.start();
// Main thread will wait until above threads get completed
latch.await();
System.out.println("Initialization has been completed, main thread can proceed now");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
|
When you run above program, you will get following output:
|
Initializing UI
Initializing Database
Initializing Logging
Done with Logging Initialization
Done with UI Initialization
Done with database Initialization
Initialization has been completed, main thread can proceed now
|
Why not use join instead of CountDownLatch:
As you might know you can use join for this situation too but you have to manually handles it. Most of people use ExecutorService for handling threads now and CountDownLatch works good with it. As CountDownLatch is task oriented , you can submit multiple tasks to thread pool and CountDownLatch will ensure execution of original thread once set of other dependent threads get completed.