You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何计算Mono.fromRunnable订阅到Runnable实际执行的时间差

实现方案

有两种常用的实现方式,可根据你的业务场景选择:

方案1:单独统计指定Mono的等待时间

利用Reactor的doOnSubscribe钩子记录订阅触发时间,在Runnable执行开头计算时间差即可,示例代码如下:
首先封装通用工具方法:

import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import reactor.core.publisher.Mono;

public class MonoTimedUtils {
    /**
     * 创建带排队时间统计的Mono<Runnable>
     * @param businessRunnable 原有业务Runnable
     * @param waitTimeConsumer 排队时间回调,入参为等待毫秒数
     * @return 包装后的Mono实例
     */
    public static Mono<Void> createTimedRunnableMono(Runnable businessRunnable, Consumer<Long> waitTimeConsumer) {
        AtomicLong subscribeTimeHolder = new AtomicLong();
        return Mono.fromRunnable(() -> {
                    // 任务拿到线程开始执行时计算等待时长
                    long waitMs = System.currentTimeMillis() - subscribeTimeHolder.get();
                    waitTimeConsumer.accept(waitMs);
                    // 执行业务逻辑
                    businessRunnable.run();
                })
                // 订阅触发时立刻记录时间
                .doOnSubscribe(subscription -> subscribeTimeHolder.set(System.currentTimeMillis()));
    }
}

替换原有代码即可使用:

// 原有逻辑直接替换为工具方法生成Mono
MonoTimedUtils.createTimedRunnableMono(new Runnable() {
    //Some business logic
}, waitMs -> {
    // 可在此处做日志打印、指标上报等统计逻辑
    System.out.printf("任务排队等待时间:%dms%n", waitMs);
})
.subscribeOn(scheduler)
.subscribe();

方案2:全局统计该线程池所有任务的等待时间

如果不需要单独统计特定Mono,想零侵入统计所有提交到该ExecutorService的任务等待时间,可以直接自定义线程池包装类,在任务提交和执行时埋点:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

public class TimedThreadPoolExecutor extends ThreadPoolExecutor {
    private final Consumer<Long> waitTimeConsumer;

    public TimedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, Consumer<Long> waitTimeConsumer) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        this.waitTimeConsumer = waitTimeConsumer;
    }

    @Override
    public void execute(Runnable command) {
        // 任务提交时记录时间
        long submitTime = System.currentTimeMillis();
        super.execute(() -> {
            // 任务执行前计算等待时长
            long waitMs = System.currentTimeMillis() - submitTime;
            waitTimeConsumer.accept(waitMs);
            command.run();
        });
    }
}

后续用这个自定义线程池构造Scheduler即可,原有业务代码无需任何修改,就能自动统计所有任务的排队等待时间。

注意事项

  • 如果你的Scheduler使用了带延迟的调度能力,统计出的时间差会包含设置的延迟时长,符合从订阅触发到实际执行的时间差统计需求。
  • 若需要更高精度的统计,可以将System.currentTimeMillis()替换为System.nanoTime(),最终结果转换为毫秒即可。

内容的提问来源于stack exchange,提问作者rand_guy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 21:54:03