当前位置: 首页 > 知识库问答 >
问题:

CompletableFuture中的静态ScheduledThreadPoolExecutor。延迟器

韩梓
2023-03-14

在java-9中,在CompletableFuture类中引入了新方法completeOnTimeout

public CompletableFuture<T> completeOnTimeout(T value, long timeout,
                                              TimeUnit unit) {
    if (unit == null)
        throw new NullPointerException();
    if (result == null)
        whenComplete(new Canceller(Delayer.delay(
                                       new DelayedCompleter<T>(this, value),
                                       timeout, unit)));
    return this;
}

我不明白的是为什么它在其实现中使用静态SchduledThreadPoolExecitor

    static ScheduledFuture<?> delay(Runnable command, long delay,
                                    TimeUnit unit) {
        return delayer.schedule(command, delay, unit);
    }

在哪里

    static final ScheduledThreadPoolExecutor delayer;
    static {
        (delayer = new ScheduledThreadPoolExecutor(
            1, new DaemonThreadFactory())).
            setRemoveOnCancelPolicy(true);
    }

对我来说,这是一种非常奇怪的方法,因为它可能会成为整个应用程序的瓶颈:唯一的schduledThreadPoolExecitor,只有一个线程保留在所有可能的CompletableFuture任务的池内?

我错过了什么?

P、 它看起来像:

>

  • 此代码的作者不愿意提取此逻辑,而是更愿意重用ScheduledThreadPoolExecutor,

    这显然导致了这样一个带有静态变量的解决方案,因为为每个可完成的未来创建一个新的执行器是非常低效的。

    但我的怀疑仍然存在,因为我发现一般方法很奇怪。

  • 共有2个答案

    窦志新
    2023-03-14

    当然,这是一个有待作者回答的问题。无论如何,这是我对这件事的看法。

    我不明白的是为什么它在其实现中使用静态schduledThreadPoolExecitor

    ...

    对我来说,这是一种非常奇怪的方法,因为它可能会成为整个应用程序的瓶颈:唯一的schduledThreadPoolExecitor,只有一个线程保留在所有可能的CompletableFuture任务的池内?

    你说得对。ScheduledThreadPoolExecutor可以运行任意代码。具体来说,orTimeout()completeOnTimeout()将异常调用completeeexceptionally()complete(),默认情况下,这会同步调用依赖项。

    为了避免这种行为,您必须使用您自己的CompletionStageCompletableFuture的子类,这使得非*Async方法始终调用*Async方法。自Java9以来,这要容易得多,因为它覆盖了newInComplete eFuture()

    它看起来像:

    1) 此代码的作者不愿意提取此逻辑,而是更愿意重用ScheduledThreadPoolExecutor,

    ForkJoinPool出现在Java7中时,它缺少一个公共线程池。Java8引入了静态公共池(),在引入的CompletableFutureStream类中默认使用(以及其他)。

    似乎他们不愿意透露一个共同的预定遗嘱执行人。这将与公共线程池一样有用,以避免部署许多很少使用的调度执行器。

    如果您需要具有静态间隔的延迟任务,那么就需要CompletableFuture。delayedExecutor()可能已经足够好了,因为包装对象的开销很小。

    对于可变间隔,每次创建包装执行器都会有额外的开销,但在创建过程中已经创建了一些对象,例如内部取消器、超时、延迟完成器和任务提交器类的新实例。

    我们多久需要在可变时间间隔内延迟许多任务?纯异步代码可能会在不同的超时情况下一直这样做,但由于我们没有暴露调度的执行程序本身,因此我们要么假设这个开销,要么使用另一个静态调度程序。

    2)这显然导致了静态变量的这种解决方案,因为为每个CompletableFuture创建一个新的执行程序效率非常低。

    确切地

    姚飞昂
    2023-03-14

    你是对的,这可能会成为瓶颈,但不是完成本身,它只是在CompletableFuture中设置一个变量。那个单线程可以在一秒钟内完成数百万个未来。关键的方面是完成可能会触发完成线程中依赖阶段的评估。

    所以

    Executor neverDone = r -> {};
    long t0 = System.nanoTime();
    CompletableFuture<String> c11 =
        CompletableFuture.supplyAsync(() -> "foo", neverDone)
            .completeOnTimeout("timeout", 2, TimeUnit.SECONDS)
            .thenApply(s -> {
                System.out.println("long dependent action 1 "+Thread.currentThread());
                LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
                return s;
            });
    CompletableFuture<String> c12 =
        CompletableFuture.supplyAsync(() -> "bar", neverDone)
            .completeOnTimeout("timeout", 2, TimeUnit.SECONDS)
            .thenApply(s -> {
                System.out.println("long dependent action 2 "+Thread.currentThread());
                LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
                return s;
            });
    System.out.println("set up");
    CompletableFuture.allOf(
        c11.thenAccept(System.out::println),
        c12.thenAccept(System.out::println)
    ).join();
    System.out.println(Math.round((System.nanoTime()-t0)*1e-9)+" s");
    

    将打印

    set up
    long dependent action 1 Thread[CompletableFutureDelayScheduler,5,main]
    timeout
    long dependent action 2 Thread[CompletableFutureDelayScheduler,5,main]
    timeout
    12 s
    

    使用...异步链接方法将消除该问题

    Executor neverDone = r -> {};
    long t0 = System.nanoTime();
    CompletableFuture<String> c11 =
        CompletableFuture.supplyAsync(() -> "foo", neverDone)
            .completeOnTimeout("timeout", 2, TimeUnit.SECONDS)
            .thenApplyAsync(s -> {
                System.out.println("long dependent action 1 "+Thread.currentThread());
                LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
                return s;
            });
    CompletableFuture<String> c12 =
        CompletableFuture.supplyAsync(() -> "bar", neverDone)
            .completeOnTimeout("timeout", 2, TimeUnit.SECONDS)
            .thenApplyAsync(s -> {
                System.out.println("long dependent action 2 "+Thread.currentThread());
                LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
                return s;
            });
    System.out.println("set up");
    CompletableFuture.allOf(
        c11.thenAccept(System.out::println),
        c12.thenAccept(System.out::println)
    ).join();
    System.out.println(Math.round((System.nanoTime()-t0)*1e-9)+" s");
    

    将打印

    set up
    long dependent action 2 Thread[ForkJoinPool.commonPool-worker-2,5,main]
    long dependent action 1 Thread[ForkJoinPool.commonPool-worker-9,5,main]
    timeout
    timeout
    7 s
    

    结论是,当您有一个可能很长的评估时,您应该始终通过其中一个异步方法来链接is。考虑到在使用不带“…Async”后缀的方法时无法控制执行线程(也可能是调用链接方法的线程或任何其他调用“完成方法”的线程,请参阅此答案),这是您始终应该做的。

     类似资料:
    • 本文向大家介绍PHP延迟静态绑定的深入讲解,包括了PHP延迟静态绑定的深入讲解的使用技巧和注意事项,需要的朋友参考一下 前言 所谓延迟静态绑定,顾名思义,静态调用时::符号左侧的部分的的绑定是延迟,也就是说不再被解析为定义当前方法所在的类,而是在实际运行时计算的。本文主要介绍了关于PHP延迟静态绑定的相关内容,下面话不多说了,来一起看看详细的介绍吧。 嗅到了坏的味道 这段时间看项目后台的PHP代码

    • 本文向大家介绍PHP延迟静态绑定示例分享,包括了PHP延迟静态绑定示例分享的使用技巧和注意事项,需要的朋友参考一下 没怎么用过这个新特性,其实也不算新啦,试试吧,现在静态类的继承很方便了

    • 本文向大家介绍php延迟静态绑定实例分析,包括了php延迟静态绑定实例分析的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了php延迟静态绑定的方法。分享给大家供大家参考。具体分析如下: php延迟静态绑定:指类的self,不是以定义时为准,而是以计算时的运行结果为准。先看一个实例   说明: (1)子类实例化对象 $stu 调用say方法时,是在父类Human内运行的,所以,say()里的

    • 我目前在react native中有一个函数,它执行以下操作: 我运行上述命令,可以确认arrayId和title变量有效并包含数据。arrayId也不是“selectProduct”。我在调试时在那里添加了一个console.log,以确保它运行,事实上确实如此。我期望的行为是状态立即更新。 但是,所选下拉列表的状态不会更新。在this.setState更新之后添加:console.log(th

    • 本文向大家介绍PHP Static延迟静态绑定用法分析,包括了PHP Static延迟静态绑定用法分析的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了PHP Static延迟静态绑定用法。分享给大家供大家参考,具体如下: PHP5.3以后引入了延迟静态绑定static,它是为了解决什么问题呢?php的继承模型中有一个存在已久的问题,那就是在父类中引用扩展类的最终状态比较困难。来看一个例子。

    • 问题内容: 我需要在循环中对数据库进行SQL查询: 更好的方法是:保持原样或循环后移动: 或者是其他东西 ? 问题答案: 整个要点是直到函数返回才执行,因此将其放置在要关闭的资源打开后的适当位置。但是,由于要在循环内创建资源,因此根本不要使用defer- 否则,在函数退出之前,您不会关闭在循环内创建的任何资源,因此它们会堆积直到然后。相反,您应该在每次循环迭代结束时关闭它们, 而无需 :