为什么 CompletableFuture.supplyAsync 成功的次数是随机的?

Joe*_*ale 1 java lambda asynchronous completable-future

我是 Java 8 中 lambdas 和异步代码的新手。我不断得到一些奇怪的结果......

我有以下代码:

import java.util.concurrent.CompletableFuture;

public class Program {

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            String test = "Test_" + i;
            final int a = i;

            CompletableFuture<Boolean> cf = CompletableFuture.supplyAsync(() -> doPost(test));
            cf.thenRun(() -> System.out.println(a)) ;
        }
    }

    private static boolean doPost(String t) {
        System.out.println(t);

        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

实际代码要长得多,因为该doPost方法会将一些数据发布到 Web 服务。但是,我可以用这个简单的代码复制我的问题。

我想让该doPost方法执行 100 次,但出于性能原因异步执行(为了将数据推送到 Web 服务的速度比执行 100 次同步调用更快)。

在上面的代码中,“doPost”方法运行了随机次数,但始终不超过 20-25 次。没有抛出异常。似乎某些线程处理机制正在默默地拒绝创建新线程并执行它们的代码,或者线程在不使程序崩溃的情况下默默地崩溃。

我也有一个问题,如果我向该doPost方法添加比上面显示的更多的功能,它会达到该方法只是默默地中断的地步。System.out.println("test")在这种情况下,我尝试在 return 语句之前添加一个right,但它从未被调用。循环 100 次的循环确实运行了 100 次迭代。

至少可以说,这种行为令人困惑。

我错过了什么?为什么将函数作为参数提供以supplyAsync运行看似随机的次数?

编辑:只是想指出这种情况与标记为可能重复的问题并不完全相同,因为该问题涉及任意深度嵌套的期货,而这个问题涉及平行期货。然而,它们失败的原因实际上是相同的。这些案例似乎足够不同,值得向我提出单独的问题,但其他人可能不同意......

rad*_*tao 5

默认情况下CompletableFuture使用自己的ForkJoinPool.commonPool()(见CompletableFuture实现)。并且这个默认池只创建守护线程,例如,如果主应用程序还活着,它们不会阻止主应用程序终止。

您有以下选择:

  1. 将所有内容收集CompletionStage到某个数组然后进行制作- 这将保证在join()之后完成所有阶段java.util.concurrent.CompletableFuture#allOf().toCompletableFuture().join()

  2. *Async操作与您自己的线程池一起使用,该线程池仅包含非守护线程,如下例所示:

    public static void main(String[] args) throws InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(10, r -> {
            Thread t = new Thread(r);
            t.setDaemon(false); // must be not daemon
            return t;
        });
    
        for (int i = 0; i < 100; i++) {
            final int a = i;
    
            // the operation must be Async with our thread pool
            CompletableFuture<Boolean> cf = CompletableFuture.supplyAsync(() -> doPost(a), pool);
            cf.thenRun(() -> System.out.printf("%s: Run_%s%n", Thread.currentThread().getName(), a));
        }
    
        pool.shutdown(); // without this the main application will be blocked forever
    }
    
    private static boolean doPost(int t) {
        System.out.printf("%s: Post_%s%n", Thread.currentThread().getName(), t);
    
        return true;
    }
    
    Run Code Online (Sandbox Code Playgroud)