Why is CompletableFuture join/get faster in separate streams than using one stream

前端 未结 3 1715
眼角桃花
眼角桃花 2021-01-02 21:51

For the following program I am trying to figure out why using 2 different streams parallelizes the task and using the same stream and calling join/get on the Completable fut

3条回答
  •  清歌不尽
    2021-01-02 22:43

    @Deadpool answered it pretty well, just adding my answer which can help someone understand it better.

    I was able to get an answer by adding more printing to both methods.

    TLDR

    • 2 stream approach: We are starting up all 6 tasks asynchronously and then calling join function on each one of them to get the result in a separate stream.

    • 1 stream approach: We are calling the join immediately after starting up each task. For example after spinning a thread for task 1, calling join makes sure the thread waits for completion of task 1 and then only spin up the second task with async thread.

    Note: Also, if we observe the output clearly, in the 1 stream approach, output appears sequential order since the all six tasks were executed in order. But during second approach all tasks were executed in parallel, hence the random order.

    Note 2: If we replace stream() with parallelStream() in the 1 stream approach, it will work identically to 2 stream approach.

    More proof

    I added more printing to the streams which gave the following outputs and confirmed the note above :

    1 stream:

    List results = sleepTimes.stream()
                    .map(sleepTime -> CompletableFuture.supplyAsync(() -> sleepTask(sleepTime), executorService2)
                            .exceptionally(ex -> { ex.printStackTrace(); return -1; }))
                    .map(f  -> {
                        int num = f.join();
                        System.out.println(String.format("doing join on task %d", num));
                        return num;
                    })
                    .collect(Collectors.toList());
    
    
    
    WITH SAME STREAM FOR FUTURE AND JOIN
    Task with sleep time 1
    doing join on task 1
    Task with sleep time 2
    doing join on task 2
    Task with sleep time 3
    doing join on task 3
    Task with sleep time 4
    doing join on task 4
    Task with sleep time 5
    doing join on task 5
    Task with sleep time 6
    doing join on task 6
    done in 21 seconds.
    [1, 2, 3, 4, 5, 6]
    

    2 streams:

    List> futures = sleepTimes.stream()
              .map(sleepTime -> CompletableFuture.supplyAsync(() -> sleepTask(sleepTime), executorService)
                      .exceptionally(ex -> { ex.printStackTrace(); return -1; }))
              .collect(Collectors.toList());
    
    List result = futures.stream()
                .map(f  -> {
                    int num = f.join();
                    System.out.println(String.format("doing join on task %d", num));
                    return num;
                })
                .collect(Collectors.toList());
    
    
    
    WITH SEPARATE STREAMS FOR FUTURE AND JOIN
    Task with sleep time 2
    Task with sleep time 5
    Task with sleep time 3
    Task with sleep time 1
    Task with sleep time 4
    Task with sleep time 6
    doing join on task 1
    doing join on task 2
    doing join on task 3
    doing join on task 4
    doing join on task 5
    doing join on task 6
    done in 6 seconds.
    [1, 2, 3, 4, 5, 6]
    

提交回复
热议问题