等待 Completable 未来线程完成的推荐方法是啥

Posted

技术标签:

【中文标题】等待 Completable 未来线程完成的推荐方法是啥【英文标题】:What is the recommended way to wait till the Completable future threads finish等待 Completable 未来线程完成的推荐方法是什么 【发布时间】:2015-08-22 17:35:11 【问题描述】:

我正在使用CompletableFuture,如下代码所示。但是关于我应该等到所有可运行对象完成的方式,我找到了两种方法,我不知道它们之间的区别,哪一种是最佳实践?它们如下:

代码

this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedSERun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);
this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedNWRun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);
this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedNERun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);
this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedSWRun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);

等待所有可运行对象完成的第一种方法

this.growSeedExecutor.shutdown();
this.growSeedExecutor.awaitTermination(1, TimeUnit.DAYS);

第二种等待所有可运行对象完成的方法

CompletableFuture.allOf(this.growSeedFutureList).join();

请告诉我推荐哪一个。

【问题讨论】:

两者都可以工作,所以这取决于你想对执行者做什么:如果你不再需要它,使用前者 - 如果你想重用它,使用后者......同样在您的第一个代码 sn-p 中,您只保留对最后一个 CompletableFuture 的引用... 我不太明白this.growSeedFutureList = growSeedFutureList 的类型是什么?这是将元素添加到列表的一些新语法吗?有人可以澄清一下吗?有没有办法在没有列表的情况下实现这一点? 【参考方案1】:

如果你真的想等待所有的期货,你可以简单地在每个期货上调用join()

growSeedFutureList.forEach(CompletableFuture::join);

与使用allOf() 相比的主要区别在于,这将在它到达以异常完成的未来时立即抛出异常,而allOf().join() 版本只会在所有未来都完成后抛出异常(异常或不是)。

另一个小的区别是这不会创建中介allOf 阶段。如果您想在所有未来完成后异步执行某些操作,而不是仅仅等待所有未来完成,这样的阶段仍然很有用。

执行者在另一边的解决方案有几个缺点:

它会阻止重复使用执行器,因为它需要关闭; 它要求您对所有操作都使用该执行程序 - 它不适用于以其他方式管理的 CompletableFutures; 它没有明确表明您的意图,即等待所有期货完成; 实现起来更复杂; 它不处理异常完成 - 如果其中一项任务失败,awaitTermination() 不会抛出异常。

【讨论】:

【参考方案2】:

仅当执行器(growSeedExecutor)仅用于给定任务时,这两种方式才等效。第一种方式可能会导致:另一个任务需要并行化,并且为每个任务创建新的执行器。一些开发者看到创建的执行器太多,决定使用单个通用执行器,但未能删除所有执行器关闭...

所以第二种方式 (join()) 更可靠,因为它不那么复杂。但是每个新的future都应该添加到growSeedFutureList中,而不是分配给。

【讨论】:

【参考方案3】:

回复有点晚了,但希望这段代码可以帮助寻找的人。这里使用通用的 forkJoin 池执行器

package com.company;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

public class CompletableFutureExample 
    public static void main(String args[])
        List<CompletableFuture> futureList=new ArrayList<>();
        for(int i=0;i<10;i++) 
            futureList.add(CompletableFuture.supplyAsync(()->getThreadName()).thenAccept(name->printThreadName(name)));
        
        futureList.forEach(CompletableFuture::join);
    

    static String getThreadName()
        String threadDetails=Thread.currentThread().getName();
        System.out.println("thread deteails::::"+threadDetails);
        return threadDetails;
    
    static void printThreadName(String value)
        System.out.println("thread string value::"+value);
    

【讨论】:

以上是关于等待 Completable 未来线程完成的推荐方法是啥的主要内容,如果未能解决你的问题,请参考以下文章

Java之多线程

关于wait/notify

关于wait/notify

利用多线程实现Future模式

java工程师面试常问的多线程问题推荐

等待未来函数完成其执行