提问人:Drake 提问时间:11/21/2020 最后编辑:Gautham ChurchillDrake 更新时间:11/21/2020 访问量:1177
Java 并发,在什么情况下 CompletableFuture.supplyAsync() 会返回 null
Java concurrency, under what condition will CompletableFuture.supplyAsync() return null
问:
在生产环境中发现一个问题,我们有一个批处理方法,如下所示:CompletableFuture.supplyAsync()
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
public class CompletableFutureProblem {
public void batchOperation(){
List<String> stringList = new ArrayList<>();
stringList.add("task1");
stringList.add("task2");
List<CompletableFuture<String>> futures = new ArrayList<>();
stringList.parallelStream().forEach(str -> {
CompletableFuture<String> response = restApiCall(str);
futures.add(response);
});
//futures.add(null);
CompletableFuture<Void> result = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
CompletableFuture<List<String>> convertedResult = result.thenApply(v ->
futures.stream().map(CompletableFuture::join).collect(Collectors.toList())
);
try {
List<String> finishedTask = convertedResult.get();
System.out.println(finishedTask.toString());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
public CompletableFuture<String> restApiCall(String str){
return CompletableFuture.supplyAsync(() -> {
return "Complete-" + str;
});
}
public static void main(String[] args) {
CompletableFutureProblem problem = new CompletableFutureProblem();
problem.batchOperation();
}
}
当它正常工作时,将打印: [完成任务 2、完成任务 1]
但是,有时它会在生产中抛出如下所示的异常:
Exception in thread "main" java.lang.NullPointerException
at java.util.concurrent.CompletableFuture.andTree(CompletableFuture.java:1320)
at java.util.concurrent.CompletableFuture.allOf(CompletableFuture.java:2238)
at third.concurrent.CompletableFutureProblem.batchOperation(CompletableFutureProblem.java:20)
at third.concurrent.CompletableFutureProblem.main(CompletableFutureProblem.java:40)
我查过源码发现,如果列表futures包含null,比如futures.add(null),就会抛出异常,但是我真的不知道in方法在什么情况下会返回呢?CompletableFuture.allOf()
CompletableFuture.supplyAsync()
restApiCall
null
感谢您的患者阅读这篇长文。
答:
5赞
spongebob
11/21/2020
#1
futures
由多个线程写入,因为您正在使用并行流。但是,它不是一个线程安全的。stringList
futures
ArrayList
因此,如果没有适当的同步,您无法确定从不同线程添加到其中的每个元素是否可见。当您将其转换为数组时,会出现内存可见性问题,这是不确定的,因此有时它会按预期工作。
若要解决此问题,通常会使用并发集合。但是,在这种情况下,并行化是没有意义的,因为它是非阻塞调用。因此,最好的解决方案是遍历列表:CompletableFuture.supplyAsync()
stringList.forEach(str -> {
此外,预分配的数组 in 应该是空的:toArray()
futures.toArray(new CompletableFuture[0])
评论
0赞
Drake
11/21/2020
请问我们如何重现此异常并确保这是由线程可见性问题引起的。
1赞
spongebob
11/21/2020
@LiJing将在一段时间后生成异常。修复后,它将无限期地继续运行。while (true) problem.batchOperation();
评论