The Roy
The Roy

Reputation: 2208

Is there a better way to wait for two threads to complete their tasks than using the CountDownLatch?

My requirement is to wait for two threads to complete execution before kickstarting a dependent job.

In order to do this, I am able to create a CountDownLatch and a Waiter Thread which will wait for the CountDownLatch to become zero. One constraint is I cannot use the main thread to wait for the two threads to complete. The main thread continues with other tasks.

This thing does work. However, I get a feel of workaround in this than a solid design.

My questions are the following:

  1. What are obvious flaws in the current approach? eg spurious signal
  2. What design would you recommend?

My current code:

class Waiter implements Runnable {
    private CountDownLatch latch; 

    Waiter (CountDownLatch latch){
        this.latch = latch;
    }

    @Override
    public void run() {
        System.out.println("Waiter Started running..." + latch.getCount());

        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Waiter ready to trigger Next Job!");
    }
}

class Processor implements Runnable {
    private CountDownLatch latch; 

    Processor (CountDownLatch latch){
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        latch.countDown();
    }
}

public class CountDownLatchDemo {
    public static void main (String[] args) throws InterruptedException{
        CountDownLatch latch = new CountDownLatch(2);

        ExecutorService executor = Executors.newFixedThreadPool(2);
        for (int i=0; i< 2; i++){
            executor.submit(new Processor(latch));
        }

        ExecutorService waitExecutor = Executors.newFixedThreadPool(2);
        waitExecutor.submit(new Waiter(latch));

        Thread.sleep(3000);
        executor.shutdown();
        waitExecutor.shutdown();
        System.out.println("Keep doing other things! Sleep here is just for help you run this code for test!");
    }
}

Upvotes: 4

Views: 3431

Answers (1)

Andrew Lygin
Andrew Lygin

Reputation: 6197

CountDownLatch is a proper solution for your task. But Java 8 provides another option – CompletableFuture. You can create two such futures for your tasks and then use one of the methods that wait for futures completion and execute something else asynchronously. For instance:

// Submit Task 1
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
    }
    System.out.println("Task 1 completed");
    return 5;
});
// Submit Task 2
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
    }
    System.out.println("Task 2 completed");
    return 7;
});
// This call will create a future that will wait for f1 and f2 to complete
// and then execute the runnable
CompletableFuture.allOf(f1, f2).thenRun(() -> {
    System.out.println("Both completed");
});

All these calls will be processed asynchronously and your main thread will continue running. If you need the results of the first two tasks in your third task, you can use thenAcceptBothAsync() instead of allOf():

f1.thenAcceptBothAsync(f2, (a, b) -> System.out.println("Result = " + (a + b)));

There're lots of methods in CompletableFuture that allow you to create chains of asynchronously executed tasks. JVM uses the default ForkJoinPool to execute them, but you can provide your own Executors to complete your futures and do a lot of other useful things with them.

Upvotes: 6

Related Questions