Suppose you face condition where main thread has to wait
until child thread finishes. Classical example of using CountDownLatch in
Java is any server side core Java application which uses services architecture,
where multiple services are provided by multiple threads and application can
not start processing until all services have started successfully. Another
scenario is where we have requirement where we have three threads
"A", "B" and "C" and we want to start thread
"C" only when "A" and "B" threads completes or
partially completes their task.
Real life example
could be where manager divided modules between development teams (A and B)
and he wants to assign it to QA team for testing only when both the teams
completes their task. Another Example, company cab driver waits until can is
full.
In these cases we
need a latch to hold main thread and open latch once child thread(threads)
finishes(latch principle).
1. This class is
available in java.util.concurrent package.
2. This class
is initialized with an integer number, which is the number of operations the
threads are going to wait for. When a thread wants to wait for the execution of
these operations, it uses the await() method.
3. This method puts the thread to sleep until the operations are completed. When one of these operations finishes, it uses the
countDown() method to decrement the internal counter of the CountDownLatch class. When the counter arrives to
0, the class wakes up all the threads that were sleeping in the await() method.
Countdownlatch class function:
1. CountDownLatch(int count) : Creates an
instance of CountDownLatch with the number of times the countDown() method must
be called before the threads waiting with await()can continue execution.
2. void await( ) : If the current count in
CountDownLatch object is zero, it immediately returns; otherwise, the thread
blocks until the countdown reaches zero. Can throw an InterruptedException
3. void countDown( ) : Reduces the number of counts by
one in this CountDownLatch object. If the count reaches zero, all the
(a)waiting threads are released. If the current count is already
zero, nothing happens.
4. long getCount( ) : Returns the pending counts in
this CountDownLatch object.
Program:
import java.util.concurrent.CountDownLatch;
public class Service implements Runnable {
private
String name="";
private
int timeToStart=0;
private
CountDownLatch latch=null;
public
Service(String name, int timeToStart, CountDownLatch latch) {
super();
this.name
= name;
this.timeToStart
= timeToStart;
this.latch
= latch;
}
public
void run() {
try{
System.out.println(name+"
before "+latch.getCount());
Thread.sleep(timeToStart);
}catch(Exception
e){
e.printStackTrace();
}
System.out.println(name+"
is up!");
latch.countDown();
System.out.println(name+"
exit "+latch.getCount());
}
}
import java.util.concurrent.CountDownLatch;
public class Main {
public
static void main(String[] args) {
final
CountDownLatch latch=new CountDownLatch(3);
System.out.println(""+latch.getCount());
Thread
initialService=new Thread(new Service("initial",5000,latch));
Thread
readingParamService=new Thread(new Service("read",1000,latch));
Thread
validationService=new Thread(new Service("validation",1000,latch));
initialService.start();
readingParamService.start();
validationService.start();
try{
latch.await();
System.out.println("All
services are up "+latch.getCount());
}catch(Exception
e){
e.printStackTrace();
}
}
}
Another Example:
public class Manager {
public static void
main(String[] args) throws InterruptedException {
CountDownLatch
countDownLatch = new CountDownLatch(2);
DevTeam
teamDevA = new DevTeam(countDownLatch, "devA");
DevTeam
teamDevB = new DevTeam(countDownLatch, "devB");
teamDevA.start();
teamDevB.start();
countDownLatch.await();
QATeam qa = new
QATeam();
qa.start();
}
}
class DevTeam extends Thread {
CountDownLatch
countDownLatch;
public DevTeam
(CountDownLatch countDownLatch, String name) {
super(name);
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.println("Task assigned to development team " +
Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch
(InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("Task finished by development team
Thread.currentThread().getName());
this.countDownLatch.countDown();
}
}
class QATeam extends Thread {
@Override
public void run() {
System.out.println("Task assigned to QA team");
try {
Thread.sleep(2000);
} catch
(InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("Task finished by QA team");
}
}
Task assigned to development team devB
Task assigned to
development team devA
Task finished by
development team devB
Task finished by
development team devA
Task assigned to
QA team
Task finished by
QA team
Here await() method waits
for countdownlatch flag to become 0, and countDown() method
decrements countdownlatch flag by 1
Limitation of JOIN: Above example can also
be achieved with join(), but join() cannot be used in two scenarios:
1. When we use ExecutorService
instead of Thread class to create threads.
2. Modify above example where
Manager wants to handover code to QA team as soon as Development completes
their 80% task. It means that CountDownLatch allow us to modify implementation
which can be used to wait for another thread for their partial execution.
Alternative to CountDownLatch is wait and notify.
Code with custom count down latch:--
public class CustomCountDownLatch {
private
int count;
public
CustomCountDownLatch(int count) {
super();
this.count
= count;
}
public
int getCount() {
return
count;
}
public
synchronized void await() throws InterruptedException {
if
(count > 0)
this.wait();
}
public
synchronized void countDown() {
count--;
if
(count == 0)
this.notifyAll();
}
}
public class MyRunnable implements Runnable {
CustomCountDownLatch
latch = null;
MyRunnable(CustomCountDownLatch
latch) {
this.latch
= latch;
}
public
void run() {
for
(int i = 1;i<=latch.getCount(); ) {
try
{
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
System.out.println(Thread.currentThread().getName()
+
" has reduced latch count to : " + i);
}
}
}
public class CountDownLatchCustomTest {
public
static void main(String[] args) {
CustomCountDownLatch
countDownLatchCustom=new CustomCountDownLatch(3);
System.out.println("CountDownLatch has been created with
count=3");
new
Thread(new MyRunnable(countDownLatchCustom),"Thread-1").start();
try
{
countDownLatchCustom.await();
} catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println("count has reached zero, "+
Thread.currentThread().getName()+"
thread has ended");
}
}