How to make one thread wait for other threads?

join 
  1. This is a legacy approach to wait for another thread's execution completion
  2. A thread has to call join() method of another thread if it has to wait on that thread
  3. Called thread will wait at join() method for the caller to complete its execution.
  4. Once the called thread completes its execution, caller thread execution resumes
In this example, main thread called join on THREAD-1 object which makes main thread to wait for THREAD-1 to complete its execution. Once THREAD-1 execution completes, main thread execution resumes.

public class JoinExample {
    public static void main(String[] args) throws InterruptedException{
        Thread thread1 = new Thread(()->{
            System.out.println(Thread.currentThread().getName()+ " is started");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+ " has completed its execution");
        }, "THREAD-1");
        thread1.start();
        System.out.println(Thread.currentThread().getName()+ " Thread is waiting for THREAD-1 completion");
        thread1.join(); // Main thread wants to wait for THREAD-1 completes its execution
        System.out.println(Thread.currentThread().getName()+ " Thread has completed its execution");

    }
}

output
main Thread is waiting for THREAD-1 completion
THREAD-1 is started
THREAD-1 has completed its execution
main Thread has completed its execution


CountDownLatch
  1. This is also another approach to make one thread wait for one or more threads to complete their executions.
  2. In this approach, we create a latch and share between the threads.
  3. Any Thread which wants to wait on other threads completion will call await() method on the latch. 
  4. When the other thread finishes their execution, they call countDown() on the same latch to inform the waiting thread to give a go.
  5. CountDownLatch has to be initialized with the no. of threads that counts down the latch. Once the latch is counted down, it cannot be reused.
In this example, both the main thread and worker thread shares a common latch. 

  1. Main thread initialized latch with 1 and called await() to wait for WORKER thread's completion
  2. WORKER thread after finishing its task called countdown() on the latch to count down the latch from one to zero.
  3. Execution of the main thread resumes. 
class CountDownLatchExample{
    public static void main(String []args){
        CountDownLatch latch = new CountDownLatch(1); //There is 1 worker thread here.
        Thread worker = new Thread(()->{
            try{
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+ " has finished its process and counting down the latch");
            latch.countDown();
        }, "WORKER");
        worker.start();
        try{
            System.out.println("main thread is waiting for the latch to be counted down ");
            latch.await();
            System.out.println("Worker thread has finished its execution after 2 sec, Main thread is resuming now");
        }catch(InterruptedException ie){
            ie.printStackTrace();
        }
    }
}

Output
main thread is waiting for the latch to be counted down 
WORKER has finished its process and counting down the latch
Worker thread has finished its execution after 2 sec, Main thread is resuming now

CyclicBarrier
  1. In this approach, multiple threads wait for each other on a barrier. After all the threads reaches the barrier, flow resumes.
  2. To wait on a barrier, a thread has to call await() on the barrier instance that is shared between threads.
  3. To cross the barrier, all the threads have to finish calling await() method on the shared barrier. Till that happens all threads has to wait for each other at the barrier await() call.
  4. Once the await() call is completed by all the threads, the barrier will be broken and the flow resumes for all the threads.
public class CyclicBarrierExample {

    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(2);
        Runner runner = new Runner(barrier);

        new Thread(runner, "Thread 1").start();
        new Thread(runner, "Thread 2").start();
    }
}

class Runner implements Runnable{

    CyclicBarrier barrier;

    public Runner(CyclicBarrier barrier) {
        this.barrier = barrier;
    }

    @Override
    public void run() {
        try{
            System.out.println(Thread.currentThread().getName() +" is waiting on barrier");
            barrier.await();
            System.out.println(Thread.currentThread().getName() +" has crossed the barrier");
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

Output
Thread 1 is waiting on barrier
Thread 2 is waiting on barrier
Thread 2 has crossed the barrier
Thread 1 has crossed the barrier

Comments

Popular posts from this blog

Distributed database design using CAP theorem

LDAP - Basics

Easy approach to work with files in Java - Java NIO(New input output)