繁体   English   中英

CompletableFuture:几个任务

[英]CompletableFuture: several tasks

如何使用5个CompletableFutures异步执行20个可运行任务(或1个任务20次)?

那就是我所拥有的:

Runnable task = () -> {
        long startTime = System.currentTimeMillis();
        Random random = new Random();

        while (System.currentTimeMillis() - startTime < 3000) {
            DoubleStream.generate(() -> random.nextDouble())
                    .limit(random.nextInt(100))
                    .map(n -> Math.cos(n))
                    .sum();
        }
        System.out.println("Done");
    };

    for (int i = 0; i < 4; i++) {
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(task);
        CompletableFuture<Void> future2 = CompletableFuture.runAsync(task);
        CompletableFuture<Void> future3 = CompletableFuture.runAsync(task);
        CompletableFuture<Void> future4 = CompletableFuture.runAsync(task);
        CompletableFuture<Void> future5 = CompletableFuture.runAsync(task);
        future1.get();
        future2.get();
        future3.get();
        future4.get();
        future5.get();
    }

如果执行此代码,我可以看到它仅异步运行3 future.get():3,然后在1 for()迭代期间剩下2

因此,我想尽可能异步地完成所有20个任务

您可以使用allOf来同时运行多个任务。 首先,我创建了一个包含5个任务的组合(与您的问题相同),但是随后我添加了10个任务(并且只失败了两次),并获得了一半的执行时间。

for (int i = 0; i < 2; i++) {
   CompletableFuture<Void> future1 = CompletableFuture.runAsync(task);
   CompletableFuture<Void> future2 = CompletableFuture.runAsync(task);
  // and so on until ten  
   CompletableFuture<Void> future10 = CompletableFuture.runAsync(task);

   CompletableFuture<Void> combined = CompletableFuture.allOf(future1, future2, future3, future4, future5, future6, future7, future8, future9, future10);

   combined.get();
}

CompletableFuture的默认执行程序是ForkJoinPool的公共池,该池具有与CPU内核数减一相匹配的默认目标并行性。 因此,如果您有四个核心,最多三个作业将异步执行。 由于您要每5个工作强制等待完成,因此您将获得三个并行执行,然后在每个循环迭代中获得两个并行执行。

如果要获得特定的执行策略(如您选择的并行性),最好的方法是指定一个正确配置的执行程序。 然后,您应该让执行者管理并行性,而不要循环等待。

ExecutorService pool = Executors.newFixedThreadPool(5);

for (int i = 0; i < 20; i++) {
    CompletableFuture.runAsync(task, pool);
}
pool.shutdown();
pool.awaitTermination(1, TimeUnit.DAYS); // wait for the completion of all tasks

这允许五个并行作业,但是将允许五个线程中的每个线程在完成一个任务后立即接一个新任务,而不是等待下一个循环迭代。

但是当你说

因此,我想尽可能异步地完成所有20个任务

目前尚不清楚为什么在计划了五个作业之后强制执行等待。 最大并行度可以通过以下方式实现

ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < 20; i++) {
    CompletableFuture.runAsync(task, pool);
}
pool.shutdown();
pool.awaitTermination(1, TimeUnit.DAYS); // wait for the completion of all tasks

这可能会产生与作业一样多的线程,除非在计划所有作业之前完成一个作业,因为在这种情况下工作线程可以选择一个新作业。

但是这种逻辑根本不需要CompletableFuture 您还可以使用:

ExecutorService pool = Executors.newCachedThreadPool();
// schedule 20 jobs and return when all completed
pool.invokeAll(Collections.nCopies(20, Executors.callable(task)));
pool.shutdown();

但是,当您的工作不涉及I / O或任何其他等待方式时。 释放CPU时,创建比CPU内核更多的线程是没有意义的。 配置为处理器数量的池是首选。

ExecutorService pool = Executors.newWorkStealingPool(
    Runtime.getRuntime().availableProcessors());
// schedule 20 jobs at return when all completed
pool.invokeAll(Collections.nCopies(20, Executors.callable(task)));
pool.shutdown();

在您的特殊情况下,这可能会运行得更慢,因为当线程多于核心时,您的作业会占用系统时间,从而看起来运行得更快,但实际上工作量却要少得多。 但是对于普通的计算任务,这将提高性能。

将以下系统属性设置为希望公共fork连接池使用的线程数:

java.util.concurrent.ForkJoinPool.common.parallelism 

参见ForkJoinPool

原因是在构造可完成的期货时未指定自己的派生联接池,因此它隐式使用

ForkJoinPool.commonPool()

参见CompletableFurure

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM