【问题标题】:How to get result from CompletableFuture<List<CustomObject>> in Java 8如何从 Java 8 中的 CompletableFuture<List<CustomObject>> 获取结果
【发布时间】:2019-12-04 19:20:45
【问题描述】:

Java 8 环境。

同时使用 CompletableFuture.allOf() 运行任务并且 然后从每个线程获取每个结果,然后将所有结果组合成一个组合结果并返回。

在下面的代码中,要获得结果 ( = List&lt;Student&gt; ),它不必是 I. 和 II 之间的代码。 他们说我需要使用 join() 但没有用

我还从

获得了一些 allOf()

Java 8 CompletableFuture.allOf(...) with Collection or List
和其他链接,但对我没有任何作用。 我想我错过了一些非常简单的部分。有人知道如何让它工作吗?

public class Test1 {
    public static void main(String[] args) {
        Test1 t = new Test1();
        Map<Major, List<Student>> allMajorStudentListMap = new HashMap<>();
        // fill out some data toallMajorStudentListMap
        t.getData(allMajorStudentListMap);
    }

    List<Student> getData(Map<Major, List<Student>> allMajorStudentListMap) {
        List<CompletableFuture<List<Student>>> completableFutures = new ArrayList<>();

        // suppose the size of completableFutures is 10
        for(Map.Entry<Major, List<Student>> entry: allMajorStudentListMap.entrySet()) {
            CompletableFuture<List<Student>> future = CompletableFuture.supplyAsync(() -> getDetailedStudents(entry));
            completableFutures.add(future);
        }

        // want to run 10 jobs concurrently --> get the 10 result and then combine these 10 results into one
        // finally want to sent the combined 10 results at one in this method

        // I. ======================= I got this code from somewhere     ==========================

        CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0]))
                .exceptionally(ex -> null)
                .join();

        Map<Boolean, List<CompletableFuture<List<Student>>>> result =
                completableFutures.stream()
                        .collect(Collectors.partitioningBy(CompletableFuture::isCompletedExceptionally));

        result.forEach((k, clist) -> {
            System.out.printf("k = " + k);

            for(CompletableFuture<List<Student>> student: clist) {

            // 3) don't know how to get only List<Student> and then print here
            // tried this and that but didn't work
            // student.get() has compile error

            }

        });

        // II. =============================================================================================


        // want to return combined List<Student>
        return ???;
    }

    List<Student> getDetailedStudents(Map.Entry<Major, List<Student>> entry) 
    {
        List<Student> studentList = new ArrayList<>();

        Major major = entry.getKey();
        String majorCode = major.getMajorCode();
        String majorName = major.getMajorName();
        List<Student> studentListList = entry.getValue();           

        studentList.addAll(getDataFromRemote(majorCode, majorName, studentList)));
        return studentList;
    }

    List<Student> getDataFromRemote(String majorCode, String majorName, List<studentList> studentList) {
        // do something and then return list of Student

        return detailedStudentList;
    }
}

【问题讨论】:

    标签: java java-8 concurrency


    【解决方案1】:

    这里我创建了一个稍微改动过的(直接使用List&lt;Student&gt; 而不是Map&lt;K,V&gt; 版本的工作示例。您可以将您的解决方案与此解决方案进行比较。

    总共查询了 5 次学生列表​​,每次都同时执行,在 3 秒的人为延迟后返回一个包含一个学生对象的完整学生列表。所以理论上如果每个都同时运行,延迟 3 秒后,所有 5 个学生对象都应该显示出来。

    如果您注意到 main 方法的开始和结束之间的时间间隔,大约是 3 秒。

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.CompletableFuture;
    import java.util.*;
    import java.util.stream.Collectors;
    
    public class CompletableFutureTest {
        private static int counter = 0;
        public static void main(String[] args) {
            System.out.println("Program started at " + new Date());
           List<Student> allStudents = new ArrayList<>();
            new CompletableFutureTest().getData(allStudents);
            for(Student st : allStudents){
                System.out.println(st.getName());
            }
            System.out.println("Program ended at " + new Date());
        }
    
        private void getData(List<Student> resultToFillIn){
            List<CompletableFuture<List<Student>>> completableFutures = new ArrayList<>();
            //for simulation purpose just running regular for loop 5 times instead of yours          
            final Integer integer = new Integer(0);
            for(int i=0; i < 5; i++){
                completableFutures.add(CompletableFuture.supplyAsync(() -> getStudentsBatch()));
            }
            CompletableFuture<List<Student>>[] cfArray = new CompletableFuture[completableFutures.size()];
            cfArray = completableFutures.toArray(cfArray);
            CompletableFuture.allOf(cfArray)
                    .exceptionally(ex ->
                    {
                        ex.printStackTrace();
                        return null;
                    }).join();
           List<CompletableFuture<List<Student>>> completedFutures = completableFutures.stream().filter(cf -> !cf.isCompletedExceptionally()).collect(Collectors.toList());
           for(CompletableFuture<List<Student>> cf : completedFutures){
               resultToFillIn.addAll(cf.join());
           }
        }
    
        private List<Student> getStudentsBatch() {
            //adding some delay
            try {
                Thread.sleep( 3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            List<Student> students = new ArrayList<>();
            Student student = new Student();
            student.setName("Student " + ++counter);
            students.add(student);
            return students;
        }
    
        public static class Student{
            private String name;
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
        }
    
        public static class Major{
            private String name;
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
        }
    }
    

    由于如上所述,CompletableFuture.allOf(...) 工作正常。

    但是,尽可能避免使用 join(),因为它会停止当前正在执行的线程。如果您选择真正的异步编程,那么您可以使用thenAccept(x -&gt; {})thenApply(x -&gt; {}) 回调方法来代替join()

    希望这对您有所帮助。

    【讨论】:

      猜你喜欢
      • 2015-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-22
      • 2021-12-01
      • 1970-01-01
      • 2018-04-12
      • 2020-05-09
      相关资源
      最近更新 更多