【问题标题】:Java 8 CompletableFuture future is waiting needlessly for other futuresJava 8 CompletableFuture 未来不必要地等待其他未来
【发布时间】:2018-10-16 13:02:45
【问题描述】:

我在下面有这个简单的代码。所有期货都应该同时开始。 future13 应该在期货 1 和 3 完成后立即运行,但在日志中我看到它等到期货 1、2、3 和 4 全部完成后才运行。为什么要等待期货 2 和 4?

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import org.junit.Test;


public class Test1 {

    private void loop(Long id, int max) {
        try {
            for (int i = 0; i < max; i++) {
                System.out.println(id);
                Thread.sleep(100);
            }
        } catch (Throwable t) {
            System.out.println(t);
        }
    }

    private CompletableFuture<Void> createConfigFuture(Long id) {
        return CompletableFuture.supplyAsync(() -> {
            loop(id, 100);
            return null;
        });
    }

    @Test
    public void testMe() {
        CompletableFuture<Void> future1 = createConfigFuture(1L);
        CompletableFuture<Void> future2 = createConfigFuture(2L);
        CompletableFuture<Void> future3 = createConfigFuture(3L);
        CompletableFuture<Void> future4 = createConfigFuture(4L);

        try {
            CompletableFuture<Void> future13 = CompletableFuture.allOf(future1, future3)
                .thenApply(v -> {
                    loop(999L, 5);
                    return null;
                });
            CompletableFuture<Void> mainFuture = CompletableFuture.allOf(future13, future2, future4);
            mainFuture.get();

        } catch (InterruptedException | ExecutionException e) {
            System.out.println(e);
        }
    }
}

【问题讨论】:

  • future13 是否总是在 future2future4 之后完成,或者它也在之后开始?由于future13 需要更多时间,而CompletableFuture.allOf 不对订单做出任何保证,您想要达到什么目的?
  • 我想知道这是否是期货 1-4 几乎同时完成的简单案例(它们都具有相同的运行时间和大致相同的开始时间)并且 CompletableFuture 背后的检查机制是只是为了慢慢区分
  • 我第二个想法是,在使用打印语句时,您可能无法区分哪个是首先执行的。所有的future1、2、3和4都是异步执行的,这就是future13虽然等待1和3正常完成,但看起来还是完成了2 和 4 也是如此。
  • 由于你没有指定Executor,它使用的是普通的ForkJoinPool。因此,指示它的大小(或 CPU 内核的数量,如果您没有指定大小)会很有用。在我的计算机上(4 核,公共池,因此大小为 3),future13future4 同时处理并首先完成。

标签: java java-8 completable-future


【解决方案1】:

在 JRE 的默认 fork-join Executor 中有一个获取执行槽的队列,所有异步任务都将在该执行槽上进行序列化。

任务#2Executor 队列中的任务#3 之前,所以在观察任务#3(以及完成任务#13)的执行之前,@ 987654327@ 应该首先获得它的执行槽。

这可能被视为链接到#2#3,但除此之外,任务之间不应有任何额外的耦合。

【讨论】:

  • 由于future13是用thenApply()(而不是thenApplyAsync())创建的,它不需要槽:它的任务将在最新的future1时立即由同一线程运行和future3 完成(如果thenApply() 在两者都已经完成后被调用,则由主线程完成)。
  • @DidierL - 你是绝对正确的。我已经更新了答案。
  • 好的,所以我应该留在thenApply() 而不是thenApplyAsync(),那么我是否有保证mainFuture.get() 将在所有期货(包括future13)完成后返回/完成?
  • 当您需要完全控制时 - 我会使用 Async 并另外将其传递给执行程序以显式使用。然后,您只需将Runnable::run 方法引用用作执行程序即可切换到同步处理(如果需要)。这样,处理图保持不变,只有“执行策略”发生变化。
猜你喜欢
  • 2016-03-11
  • 2020-06-08
  • 2020-08-11
  • 1970-01-01
  • 2019-10-08
  • 2021-03-26
  • 2015-01-07
  • 2021-07-01
  • 2020-10-11
相关资源
最近更新 更多