[英]Java concurrency, under what condition will CompletableFuture.supplyAsync() return null
Found a problem in production environment regarding the CompletableFuture.supplyAsync()
We have a batch processing method like below:在生产环境中发现
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();
}
}
When it works all right will print: [Complete-task2, Complete-task1]当它工作正常时会打印:[Complete-task2, Complete-task1]
However, sometimes it throws exception like below in production:但是,有时它会在生产中抛出如下异常:
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)
I have investigated the CompletableFuture.allOf()
source code found that if the list futures contains null, for example, futures.add(null) , the exception will throw, but I really do not know under what scenarios will the CompletableFuture.supplyAsync()
in restApiCall
method return null
?查了
CompletableFuture.allOf()
源码发现如果 list futures包含空值,比如futures.add(null)会抛出异常,但是我真的不知道在什么情况下CompletableFuture.supplyAsync()
在restApiCall
方法中返回null
?
Thank you for your patient for reading the long post.感谢您的耐心阅读长篇文章。
futures
is being written to by multiple threads, since you are consuming stringList
with a parallel stream. futures
正在由多个线程写入,因为您正在使用并行流使用stringList
。 However futures
is an ArrayList
, which is not thread-safe.但是
futures
是一个ArrayList
,它不是线程安全的。
Therefore, you can't be sure that each element added to it from a different thread will be visible without proper synchronization.因此,如果没有适当的同步,您无法确保从不同线程添加到它的每个元素都可见。 When you transform it into an array, there will be memory visibility problems, which are undeterministic, hence why sometimes it works as expected.
当您将其转换为数组时,会出现内存可见性问题,这是不确定的,因此有时它会按预期工作。
To fix this problem, normally a concurrent collection would be used.为了解决这个问题,通常会使用并发集合。 However in this case, it does not make sense to parallelize
CompletableFuture.supplyAsync()
, since it is a non-blocking call.然而,在这种情况下,并行化
CompletableFuture.supplyAsync()
没有意义,因为它是一个非阻塞调用。 Therefore, the best solution is to just loop through the list:因此,最好的解决方案是循环遍历列表:
stringList.forEach(str -> {
Also, the pre-allocated array in toArray()
should be empty:此外,
toArray()
预先分配的数组应该是空的:
futures.toArray(new CompletableFuture[0])
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.