4

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!");
    }
}
Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122
The Roy
  • 2,178
  • 1
  • 17
  • 33
  • 1
    This looks fine to me. A lower level alternative would be to call join() on both threads. – JB Nizet Sep 17 '16 at 12:07
  • Join would need to be in the main thread which I want to avoid since I will need the thread to keep doing other stuff. – The Roy Sep 17 '16 at 12:08
  • 1
    using CountDownLatch is a totally valid approach in this case no need to make it more complex than that – Nicolas Filotto Sep 17 '16 at 12:09
  • 1
    No, you can pass the two worker threads to the waiter thread, and call join() from the waiter thread. But as I said, using a CountDownLatch is perfectly OK. – JB Nizet Sep 17 '16 at 12:12
  • 1
    The only remark I would make is to call countDown() in a finally block otherwise your waiter could wait forever – Nicolas Filotto Sep 17 '16 at 12:12
  • @NicolasFilotto thank you. Makes complete sense – The Roy Sep 17 '16 at 12:15
  • @JBNizet I'm afraid your approach cannot work in this case as the OP uses a thread pool so 1. you have no access to the thread directly and 2. the thread won't die once the task is over so join will make the current thread wait forever – Nicolas Filotto Sep 17 '16 at 12:15

1 Answers1

6

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.

Andrew Lygin
  • 6,077
  • 1
  • 32
  • 37