【问题标题】:Java Waiting for all threads to completeJava 等待所有线程完成
【发布时间】:2017-02-16 19:17:31
【问题描述】:

编辑:我的问题不同,它与链接的问题无关。

我使用完成处理程序跟踪代码。

FutureTask<Void> futureTask = new FutureTask<Void>(() -> {
    System.out.println("callback");
    return null;
});

Runnable task = () -> {
    for(int i=0; i<5; i++) {
        System.out.println(Thread.currentThread().getName() + " " + i);
    }
    futureTask.run();
};

new Thread(task).start();
new Thread(task).start();

基本上我正在寻找可变数量任务的完成处理程序,还是有其他方法?

我从这个answer 中得到启发,但在我寻找原生解决方案时,它似乎是某个库的一部分。

Completable Future???

这是我在最后使用结果处理程序的可完成期货的尝试。

public void test() {
    CompletableFuture
            .supplyAsync(() -> method1())
            .supplyAsync(() -> method2())
            .supplyAsync(() -> result());
}

public String method1() {
    System.out.println("calling 1");
    return "method1";
}

public String method2() {
    System.out.println("calling 2");
    return "method2";
}

public String result() {
    System.out.println("result");
    return "result";
}

【问题讨论】:

  • 为什么不能用旧方法替换futureTask.run();
  • 你基本上可以通过实现一个在其前身上调用Thread.join() 然后做任何你想做的事情来链接你的任务。
  • @Ravindrababu 不是重复的。

标签: java multithreading java-8 completable-future futuretask


【解决方案1】:

假设您的方法 result() 返回一个您想要检索的值,即声明为 Type result(),您可以使用

CompletableFuture<Type> f = CompletableFuture.allOf(
    CompletableFuture.runAsync(() -> method1()),
    CompletableFuture.runAsync(() -> method2())
).thenApply(_void -> result());

每个runAsync 创建一个单独的异步CompletableFuture,一旦Runnable 执行完毕,该异步CompletableFuture 就会完成。和supplyAsync一样,只是不返回结果。

allOf 创建一个CompletableFuture,一旦所有指定的futures 完成,它就会完成,因此,任何链接的依赖动作只会在所有futures 都完成后运行。通过使用thenApply,我们创建了一个依赖未来,它将以result()的返回值完成。

如果result() 不打算返回一个值,而只是一个应该在所有其他操作完成后运行的操作,您可以使用

CompletableFuture.allOf(
    CompletableFuture.runAsync(() -> method1()),
    CompletableFuture.runAsync(() -> method2())
).thenRun(() -> result());

改为。

【讨论】:

    【解决方案2】:

    一种简单的方法是将您的 Runnable(s) 提交给 ExecutorService,然后调用 shutdown,然后调用 awaitTermination

    ExecutorService executor = Executors.newWorkStealingPool();
    executor.submit(task);
    executor.submit(task);
    
    executor.shutdown();
    executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
    

    无需使用 CompletableFuture。

    【讨论】:

    • 你能不能把它设为不同的调用者和不同的响应者,执行者将是不同的方法,结果回调将在不同的方法中。
    【解决方案3】:

    根据您想要的控制程度,您可以使用 ThreadPoolExecutor:

    tpe.execute(Runnable);

    等待活动计数 == 0;

    然后关闭执行器。

    或者将线程保持在一个结构中。

    等待特定的 TTL,如果状态为 RUNNABLE,则中断它们

    【讨论】:

    • 正确。似乎 isTerminated() 可能是等待的方法。我打算将此添加到我的答案中,但认为这会使事情过于复杂
    • 这取决于一个人想要如何处理它们。我倾向于避免使用游泳池,因为我宁愿保留在需要时拔掉插头的能力
    • 如果您有ThreadPoolExecutor,只需使用invokeAll 提交任务列表并等待其完成。无需使用活动计数或关机等杂项。
    • 如果你有一个列表,但如果你没有,比如动态构建可运行文件,这就是你要做的。没有杂物。但是,如果其中一个可运行对象从未放弃控制,那么您的想法就会被卡住。这就是我很少使用池的原因。或者有一个有效计数的 TTL
    【解决方案4】:

    保存指向您创建的线程的链接,然后调用join()

    Thread a = new Thread(task);
    Thread b = new Thread(task);
    a.start();
    b.start();
    
    a.join();
    b.join();
    //guaranteed that both threads have completed here
    

    【讨论】:

    • 它会在那里阻塞,我想调用这些线程并忘记它,方法返回,我希望在回调中跟进。谢谢你教我join虽然
    • @user2727195 最好使用另一个线程然后运行连接和后续回调
    • 好的,你说得有道理。只是为了在这里扩展知识,您知道 CompletableFuture 吗?它是否适用于我的问题?
    • @user2727195 但是这里真的没有Future...你不是在寻找结果,只是想在两个线程都完成后等待开始下一步。所以不,它不适用于这里......换句话说,“你没有未来的孩子!”呸呸呸呸呸呸呸呸呸呸呸呸
    • 大声笑不用担心,但我对其他方法持开放态度,我的问题与此代码无关
    猜你喜欢
    • 2015-04-17
    • 1970-01-01
    • 2010-09-20
    • 1970-01-01
    • 1970-01-01
    • 2011-06-09
    • 1970-01-01
    • 2021-03-30
    相关资源
    最近更新 更多