【问题标题】:Is there a better way to wait for two threads to complete their tasks than using the CountDownLatch?有没有比使用 CountDownLatch 更好的方法来等待两个线程完成它们的任务?
【发布时间】:2016-09-17 12:00:19
【问题描述】:

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

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

这件事确实有效。但是,我觉得在这方面的解决方法比可靠的设计要好。

我的问题如下:

  1. 当前方法有哪些明显的缺陷?例如杂散信号
  2. 您会推荐什么设计?

我当前的代码:

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

【问题讨论】:

  • 这对我来说很好。较低级别的替代方法是在两个线程上调用 join()。
  • 加入需要在我想避免的主线程中,因为我需要线程来继续做其他事情。
  • 在这种情况下使用 CountDownLatch 是一种完全有效的方法,不需要让它变得更复杂
  • 不行,你可以把两个工作线程传给服务员线程,从服务员线程调用join()。但正如我所说,使用 CountDownLatch 完全没问题。
  • 我唯一想说的是在 finally 块中调用 countDown() 否则你的服务员可能会永远等待

标签: java multithreading locking


【解决方案1】:

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 来执行它们,但你可以提供自己的Executors 来完成你的未来并用它们做很多其他有用的事情。

【讨论】:

  • 谢谢。这看起来更干净。
猜你喜欢
  • 2010-11-05
  • 2012-03-23
  • 2013-08-10
  • 2016-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多