【问题标题】:Is it possible to schedule a CompletableFuture?是否可以安排 CompletableFuture?
【发布时间】:2020-03-01 14:40:22
【问题描述】:

有没有办法在 Java 中安排 CompletableFuture? 我想要做的是安排一个任务以一些延迟执行,并将它与其他操作链接起来,以便在它完成时异步执行。到目前为止,我还没有找到任何方法来做到这一点。

对于好的 ol' Futures,我们有例如ScheduledExecutorService,我们可以在其中安排一个任务以延迟执行,如下所示:

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
Future<String> future = scheduledExecutorService.schedule(() -> "someValue", 10, TimeUnit.SECONDS);

CompletableFutures 有没有类似的方法?

【问题讨论】:

    标签: java concurrency scheduled-tasks completable-future


    【解决方案1】:

    As said,Java 9 支持。

    但在 Java 8 下创建类似的功能并不难;您已经命名了必要的元素:

    // prefer this constructor with zero core threads for a shared pool,
    // to avoid blocking JVM exit
    static final ScheduledExecutorService SCHEDULER = new ScheduledThreadPoolExecutor(0);
    static Executor delayedExecutor(long delay, TimeUnit unit)
    {
      return delayedExecutor(delay, unit, ForkJoinPool.commonPool());
    }
    static Executor delayedExecutor(long delay, TimeUnit unit, Executor executor)
    {
      return r -> SCHEDULER.schedule(() -> executor.execute(r), delay, unit);
    }
    

    可以与 Java 9 功能类似地使用:

    Executor afterTenSecs = delayedExecutor(10L, TimeUnit.SECONDS);
    CompletableFuture<String> future 
      = CompletableFuture.supplyAsync(() -> "someValue", afterTenSecs);
    
    future.thenAccept(System.out::println).join();
    

    必须注意避免共享调度执行器的线程阻止 JVM 终止。零核心池大小的替代方法是使用守护线程:

    static final ScheduledExecutorService SCHEDULER
      = Executors.newSingleThreadScheduledExecutor(r -> {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
      });
    

    【讨论】:

    • 谢谢!很高兴知道自 java 9 以来有一个内置功能,但我使用的是 8,所以你展示的示例正是我所需要的
    • @Woodz 是我的一个失误,当然,runnable 应该在指定的executor 上在经过的时间之后执行,而不是ScheduledThreadPoolExecutor
    • @Spring 你可以使用你喜欢的任何池,但是因为commonPool()CompletableFuture 的默认池,当没有指定池并且我的方法旨在尽可能接近方法时在 JDK 9 中引入,我使用相同的默认值。请注意,仍然存在接受任意执行程序的重载。
    • @Spring 当您提交给延迟的执行者时,您可以立即继续执行其他操作。当您在提交之前使用sleep 时,您将阻塞启动线程。当您将sleep 包含在实际操作中时,您将阻止一个工作线程,否则该工作线程本来可以从事不同的工作。当你只做一次和/或没有其他工作要处理时,两者都无关紧要,但当你做很多事情时,它会降低性能。此外,当所有工作线程都被sleep 阻塞时,可能会发生另一个本应比睡眠工作更早运行的作业无法运行的情况。
    • @Spring 我并不反对使用ScheduledThreadPoolExecutor; newSingleThreadScheduledExecutor 只是一种选择。但这只是内部使用的执行器,用于在正确的时间将作业提交给实际的执行器,而不是实际作业的执行器。实际作业的执行者是commonPool(),如果没有指定,或者您传递给delayedExecutor 方法的任何执行者(这超出了这个答案的范围;没有给出建议)。不要混淆这两个执行者。请注意,Java 9+ 已经内置了这个功能。
    【解决方案2】:

    如果您使用的是 Java 9+,那么 CompletableFuture#delayedExecutor(long,TimeUnit) 可能适合您的需求:

    返回一个新的 Executor,它在给定的延迟后(如果非正则没有延迟)将任务提交给默认的 executor。每次延迟都从调用返回的执行程序的 execute 方法开始。

    Executor delayed = CompletableFuture.delayedExecutor(10L, TimeUnit.SECONDS);
    CompletableFuture.supplyAsync(() -> "someValue", delayed)
        .thenAccept(System.out::println)
        .join();
    

    还有an overload,您可以在其中指定Executor 来代替“默认执行程序”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-15
      • 2021-11-14
      • 2021-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多