Monday, 25 July 2016

Java CountDownLatch API

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");
            }
}



No comments:

Post a Comment