有没有比使用CountDownLatch更好的方法来等待两个线程来完成他们的任务?

时间:2016-09-17 12:00:20

标签: java multithreading locking

我的要求是在启动依赖作业之前等待两个线程完成执行。

为了做到这一点,我可以创建CountDownLatchWaiter Thread,等待CountDownLatch变为零。一个约束是我不能使用主线程来等待两个线程完成。主线程继续执行其他任务。

这件事确实有效。但是,我在这方面有一种解决方法,而不是坚实的设计。

我的问题如下:

  1. 目前的方法有哪些明显的缺陷?例如,假信号
  2. 你会推荐什么样的设计?
  3. 我目前的代码:

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

1 个答案:

答案 0 :(得分:2)

CountDownLatch是适合您的任务的正确解决方案。但Java 8提供了另一种选择 - CompletableFuture。您可以为任务创建两个这样的期货,然后使用等待期货完成的方法之一并异步执行其他操作。例如:

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

所有这些调用都将异步处理,您的主线程将继续运行。如果您需要第三项任务中前两项任务的结果,则可以使用thenAcceptBothAsync()代替allOf()

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

CompletableFuture中有许多方法允许您创建异步执行的任务链。 JVM使用默认的ForkJoinPool来执行它们,但是你可以提供自己的Executor来完成你的未来,并用它们做很多其他有用的事情。