1. Introduction

This post would demo how to do the thread communication by using CountDownLatch. If you want to know the wait-notify communication, you can refer to the previous article, if you want to learn how to communicate through the volatile keyword, you can refer to this article

1. Environments

  • Java 1.8

2.1 The CountDownLatch introduction

By using CountDownLatch , you can block the main thread to wait for all children threads done.

As this picture shows:

20180517_thread_countdownlatch

  • Main thread construct a CountDownLatch(3) and starts all children threads
  • Main thread calls the latch.await() to wait for all children thread done
  • Thread 1/2/3 done at different time(They call latch.countDown())
  • Main thread resumed when the latch count changes to 0

2.2 The CountDownLatch classic example

Here is an example of the CountDownLatch classic usage:

CountDownLatch classic usage scenarioes:

Achieving Maximum Parallelism : Sometimes we want to start a number of threads at the same time to achieve maximum parallelism. For example, we want to test a class for being singleton. This can be done easily if we create a CountDownLatch with initial count 1, and make wait all threads to wait of latch. A single call to countDown() method will resume execution for all waiting threads in same time.

Wait N threads to completes before start execution: For example an application start-up class want to ensure that all N external systems are Up and running before handling the user requests.

Deadlock detection: A very handy use case in which you can use N threads to access a shared resource with different number of threads in each test phase, and try to create a deadlock.

public static void main(String[] args) throws InterruptedException {
    int n = 3;
    CountDownLatch latch = new CountDownLatch(n);
    for(int i=0;i<n;i++) {
        (new Worker("worker"+i,(i+1)*200,latch)).start();
    }
    latch.await();
    System.out.println("all workers done");
}

private static class Worker extends Thread {
    private final long sleepTime;
    private final CountDownLatch latch;

    public Worker(String name, long sleepTime, CountDownLatch latch) {
        super(name);
        this.sleepTime = sleepTime;
        this.latch = latch;
    }
    public void run() {
        try {
            Thread.sleep(sleepTime);
            System.out.println(getName()+" stopped");
        }catch (Exception ex) {
            ex.printStackTrace();
        }finally {
            latch.countDown();
        }
    }
}

Run the above code ,we got this result:

worker0 stopped
worker1 stopped
worker2 stopped
all workers done

You can see that main thread resumed after all threads done.

2.3 CountDownLatch with ExecutorService(ThreadPool) example

What if we put the worker thread in a pool, here is the code:

public static void main(String[] args) throws InterruptedException {
    int n = 3;
    CountDownLatch latch = new CountDownLatch(n);
    ExecutorService pool
            = Executors.newFixedThreadPool(n);
    for(int i=0;i<n;i++) {
        pool.submit(new Worker("worker"+i,(i+1)*200,latch));
    }
    latch.await();
    pool.shutdown();//you must call this, because all pool thread is NON-DAEMON, main thread would not exit
    System.out.println("all workers in pool stopped");
}

private static class Worker implements Runnable {
    private final long sleepTime;
    private final CountDownLatch latch;
    private final String name;

    public Worker(String name, long sleepTime, CountDownLatch latch) {
        this.name = name;
        this.sleepTime = sleepTime;
        this.latch = latch;
    }
    public void run() {
        try {
            Thread.sleep(sleepTime);
            System.out.println(name+" stopped");
        }catch (Exception ex) {
            ex.printStackTrace();
        }finally {
            latch.countDown();
        }
    }
}

As the code shows:

  • We create a fixed size thread pool via Executors class and submit the worker threads to it
  • We call latch.await() to wait for threads done
  • The worker thread just sleep for a while and then call latch.countDown()
  • When latch.getCount()=0, the main thread resumed
  • We call pool.shutdown() to close the pool, otherwise, because all thread in the ThreadPool is not daemon, so the main thread would not exit automatically
  • After we print the all workers in pool stopped message, main thread exited ok

Here is the output:

worker0 stopped
worker1 stopped
worker2 stopped
all workers in pool stopped

Process finished with exit code 0

2.4 CountDownLatch with the await timeout settings example

There is a overloaded version of await method in the CountDownLatch which support to wait for limited time period, after that time, it would resume ignoring the latch status.

await(long timeout, TimeUnit unit) Causes the current thread to wait until the latch has counted down to zero, unless the thread is interrupted, or the specified waiting time elapses.

Here is the example code:

public static void main(String[] args) throws InterruptedException {
    int n = 3;
    CountDownLatch latch = new CountDownLatch(n);
    for(int i=0;i<n;i++) {
        (new Worker("worker"+i,(i+1)*1200,latch)).start();
    }
    try {
        latch.await(2, TimeUnit.SECONDS); //line 1
        System.out.println("all workers done or timed out");
    }catch (Exception ex) {
        ex.printStackTrace();
    }

}

private static class Worker extends Thread {
    private final long sleepTime;
    private final CountDownLatch latch;

    public Worker(String name, long sleepTime, CountDownLatch latch) {
        super(name);
        this.sleepTime = sleepTime;
        this.latch = latch;
        setDaemon(true);//would cause main thread to exit if all threads stop or coutndown latch timeout
    }
    public void run() {
        try {
            Thread.sleep(sleepTime);
            System.out.println(getName()+" stopped");
        }catch (Exception ex) {
            ex.printStackTrace();
        }finally {
            latch.countDown();
        }
    }
}
  • line 1: The latch wait for at max 2 seconds,after 2 seconds, if there still exists some thread not completed, it would just abort and resume the main thread
  • Attention: No exception would be thrown if the timeout occurs

Here is the output:

worker0 stopped
all workers done or timed out

Process finished with exit code 0

As you can see, only worker0 completed before timeout, the main thread resumed and stopped immediately after the timeout.

3. Summary

The CountDownlatch is very easy to use, however, it has shortcomings: The disadvantage of CountDownLatch is that it’s not reusable: once the count become zero it is no longer usable.

You can find the whole code examples on github project, the class source code is located at this, they are named like ThreadComm3_*.java

You can find detail documents about the java stream here: