简体   繁体   English

Java并发,什么情况下CompletableFuture.supplyAsync()会返回null

[英]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.

相关问题 使用 Mockito 测试 CompletableFuture.supplyAsync - Testing CompletableFuture.supplyAsync with Mockito 如何将completableFuture.supplyAsync()的返回类型分配给对象? - how to assign the return type of completableFuture.supplyAsync() to the object? ForkJoinPool在CompletableFuture.supplyAsync()中的行为 - Behaviour of ForkJoinPool in CompletableFuture.supplyAsync() CompletableFuture.supplyAsync() 没有 Lambda - CompletableFuture.supplyAsync() without Lambda 获取CompletableFuture.supplyAsync的结果 - Getting the result of a CompletableFuture.supplyAsync CompletableFuture.supplyAsync与新的CompletableFuture() - CompletableFuture.supplyAsync vs new CompletableFuture() 简单的 CompletableFuture.supplyAsync() 导致 IllegalMonitorStateException 错误 - Simple CompletableFuture.supplyAsync() leads to IllegalMonitorStateException error 使用CompletableFuture.supplyAsync返回多个值 - Returning multiple values with CompletableFuture.supplyAsync CompletableFuture.supplyAsync 代码的代码覆盖率 - code coverage of CompletableFuture.supplyAsync code Feign Client : 使用 jar 运行 (CompletableFuture.supplyAsync call Feign Client throw java.lang.IllegalArgumentException) - Feign Client : use jar running (CompletableFuture.supplyAsync call Feign Client throw java.lang.IllegalArgumentException)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM