Java 为什么主线程不等待其他异步进程(线程)完成。allOff工作不正常

Java 为什么主线程不等待其他异步进程(线程)完成。allOff工作不正常,java,multithreading,asynchronous,completable-future,Java,Multithreading,Asynchronous,Completable Future,我在for循环中调用一个异步方法,并将它的未来添加到一个列表中。我不知道为什么阿洛夫不等待完成所有的期货交易并返回部分结果。看看我的代码 我有一个被重写的方法 @Overide @Async CompletableFuture<someType> fetchData() { returns new CompletableFuture<someType>(); } @Overide @异步的 CompletableFuture fetchData() { 返回新的Comp

我在for循环中调用一个异步方法,并将它的未来添加到一个列表中。我不知道为什么阿洛夫不等待完成所有的期货交易并返回部分结果。看看我的代码

我有一个被重写的方法

@Overide
@Async
CompletableFuture<someType> fetchData()
{
returns new CompletableFuture<someType>();
}
@Overide
@异步的
CompletableFuture fetchData()
{
返回新的CompletableFuture();
}
我使用不同的实例在for循环中调用上述方法

获取实现一个接口的所有bean,该接口具有mehod FETCHTDATA

Map<String, SomeClass> allBeans =context.getBeansOfType(SomeClass.class);

List<SomeClass> list=
    allBeans.values().stream().collect(SomeClass.toList());

for (SomeClass classInstance: list) {
  classInstance.fetchData().thenApply(x->{
    //Some DB persistence Call
    futureList.add(x);
  });
}
Map allBeans=context.getBeansOfType(SomeClass.class);
列表=
allBeans.values().stream().collect(SomeClass.toList());
对于(SomeClass classInstance:list){
classInstance.fetchData()。然后应用(x->{
//一些数据库持久性调用
未来列表。添加(x);
});
}
在这之后,我将应用allOff,这样所有的未来都可以完成,但它并不是在等待所有线程和主线程执行流的其余部分

CompletableFuture<Void> combinedFutures = CompletableFuture.allOf(
      futureList.toArray(new CompletableFuture[futureList.size()]));

  CompletableFuture<List<futureResponse>> finalList=
      combinedFutures.thenApply(v -> {
        return futureList.stream().map(m -> 
        m.join()).collect(Collectors.toList());
       });
CompletableFuture combinedFutures=CompletableFuture.allOf(
toArray(新的CompletableFuture[futureList.size()]);
完全未来金融学家=
组合期货。然后应用(v->{
返回futureList.stream().map(m->
m、 join()).collect(Collectors.toList());
});
finalList-在这个列表中,我希望fetch返回所有已完成的期货 调用

在finalList中,我总是得到2个对象,但fetchData运行了5次(基于实例的数量),在所有剩余的异步调用完成后,我看到了日志。有人能帮忙吗


观察:-将主线程置于睡眠状态30秒后,我可以看到列表中有所有5个对象。有人能告诉我为什么主线程没有在allOff等待所有期货完成。

你有一个竞态条件:

classInstance.fetchData().thenApply(x->{
    futureList.add(x);
});
此代码意味着只有当未来
x
完成时,
x
才会添加到
futureList
。这可能在10毫秒或2小时内,谁知道呢?(如果未来例外地失败,这可能永远不会发生)

所以,当代码到达

CompletableFuture.allOf(futureList....
无法“保证”调用了
然后应用
<代码>未来列表甚至可能为空

纠正此代码的一种方法如下:

Map<String, SomeClass> allBeans =context.getBeansOfType(SomeClass.class);


List<SomeClass> list=
    allBeans.values().stream().collect(SomeClass.toList());

 for (SomeClass classInstance: list) {
    futureList.add(classInstance.fetchData());
 }

这样,您的
futureList
不是在异步结果返回时填充的(这是未知的,甚至可能会失败,并且永远不会发生异常),而是在创建异步调用时填充的,这是您真正想要的。

IIUC,您想要做的事情可以通过执行更简单的操作来完成

CompletableFuture<List<FutureResponse>> = CompletableFuture.supplyAsync(() -> {
    // start fetches and collect the individual futures
    List<CompletableFuture> fetches = 
       allBeans.values().stream()
               .map(SomeClass::fetchData)
               .collect(toList());
    // join all futures and return the list of the results
    return fetches.stream()
               .map(CompletableFuture::join)
               .collect(toList());
}
CompletableFuture=CompletableFuture.supplyAsync(()->{
//开始提取并收集单个期货
列表获取=
allBeans.values().stream()
.map(SomeClass::fetchData)
.collect(toList());
//加入所有futures并返回结果列表
return fetches.stream()
.map(CompletableFuture::join)
.collect(toList());
}

我认为您不能在单个流中执行此操作(即映射到
fetch
,然后立即映射到
join
),因为这可能会在创建下一个未来之前等待连接。

为什么要执行第二个循环?我会执行
allBeans.values().stream().map(SomeClass::fetchData.collect)(toList())
,它直接为您提供了
CompletableFuture
s的列表。@daniu您可能是对的,但我的印象是thenApply是必要的,或者
futureList
是一个悬而未决的变量(我们从未在问题中声明过它),所以我对问题代码进行了最低限度的结构修改。@GPI如果任何fetchData调用引发异常,我如何停止所有异步进程以停止其执行?@Deephrathore这是一个完全不同的问题。请看一看,或者此代码正在阻塞(连接将由调用线程执行)。而在问题代码中,连接是在
combinedFutures
完成后执行的,这意味着
join
调用不会被阻止。因此,我认为此答案的执行方式与原始问题的工作方式不同-无法抵抗错误。@GPI否,连接将异步执行轻率地(也就是说,在由
supplyAsync
创建的
CompletableFuture
中。我不知道
join
s阻塞是怎么一个问题,您可以等待
fetch
完成。但是,您是对的,这可能会执行一些不同的操作,因为收集器线程也将在公共线程池中运行。对不起,我根本没有注册
supplyAsync
行(由于某种原因,我的思维跳过了它)。阻塞可能是一个问题,也可能不是,但它是一个不同的问题。原始问题代码应该是完全非阻塞的,在这里,我们有一个阻塞调用(不是在主线程中,正如我错误地报告的,但仍然有一个阻塞调用),正如您所解释的。@daniu:-基于扩展fetchData接口的类,可能会有任意数量的fetchData调用。目前我有5个调用,将来可能会增加。什么是
futureList
?在问题的第一部分,您似乎在异步向其中添加
someType
实例(在
中,然后应用
,这似乎不是线程安全的,除非
未来列表
是某种并发集合)而在第二部分中,它似乎是一个
列表
。可能是@DidierL-futureList的副本,我将在一个列表中收集fetch调用返回的所有对象,以便在所有异步调用结束后,最终我应该拥有一个对象列表。我希望所有的fetch调用都不阻塞,这就是我没有加入它的原因。@deephratho你很好奇为什么你说你在寻找一个非阻塞的解决方案,并接受其中一个
CompletableFuture<List<FutureResponse>> = CompletableFuture.supplyAsync(() -> {
    // start fetches and collect the individual futures
    List<CompletableFuture> fetches = 
       allBeans.values().stream()
               .map(SomeClass::fetchData)
               .collect(toList());
    // join all futures and return the list of the results
    return fetches.stream()
               .map(CompletableFuture::join)
               .collect(toList());
}