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

Vert.x executeBlocking两种执行行为差异原因咨询

理解Vert.x executeBlocking与setPeriodic的行为差异

这是个非常典型的Vert.x异步编程陷阱,我来帮你拆解两种实验的本质区别:

核心原因:Event Loop上下文串行化 + 变量捕获问题

第一组实验的问题根源

你用vertx.setPeriodic每秒触发的任务,全程在同一个Event Loop线程里执行。而默认调用executeBlocking时,ordered参数为true——Vert.x会把所有来自这个Event Loop上下文的阻塞任务放到一个串行队列中,前一个阻塞任务没执行完,后面的任务只能排队等待。

再加上你的blockingMethod要耗时2秒,触发频率却是每秒1次,就会出现连锁问题:

  • 第1秒:counter递增为1,提交阻塞任务A到队列并开始执行
  • 第2秒:counter递增为2,提交阻塞任务B到队列排队
  • 第3秒:counter递增为3,提交阻塞任务C到队列排队
  • 第3秒末:任务A执行完成,若你的代码里直接引用全局counter变量输出结果,此时拿到的已经是3(而非触发时的1),你会误以为任务A的结果丢失了
  • 后续任务执行时,同样会拿到最新的counter值,最终看起来就像“每2次触发仅输出1次结果”

本质上任务并没有丢失,只是串行执行导致任务执行时的变量值被后续Event Loop的修改覆盖,加上任务堆积延迟,让你产生了“事件丢失”的错觉。

第二组实验为什么正常

你把阻塞逻辑封装到AsyncClass的result方法里,通过wrapperMethod触发executeBlocking,核心变化在于:

  • 每次触发时,你应该是把触发瞬间的counter值封装到了AsyncClass实例中(比如通过构造参数传递),而非引用全局共享的counter变量
  • 或者wrapperMethod在调用executeBlocking时,用局部变量捕获了触发时的counter值,再传递给阻塞逻辑

这样每个阻塞任务持有的都是独立的、触发时的变量快照,不会被后续Event Loop的修改覆盖。哪怕还是用默认的ordered=true串行执行,每个任务的结果也能和触发顺序一一对应,自然不会出现“丢事件”的情况。

代码示例对比

错误的第一组写法(导致变量覆盖)

AtomicInteger counter = new AtomicInteger(0);
vertx.setPeriodic(1000, id -> {
    counter.incrementAndGet();
    vertx.executeBlocking(future -> {
        blockingMethod();
        future.complete(counter.get()); // 执行时拿到的是最新counter值,而非触发时的
    }, result -> {
        System.out.println("结果:" + result.result());
    });
});

正确的第二组写法(保存触发时的变量快照)

AtomicInteger counter = new AtomicInteger(0);
vertx.setPeriodic(1000, id -> {
    int currentCount = counter.incrementAndGet();
    AsyncClass asyncTask = new AsyncClass(currentCount); // 传递触发时的count值
    asyncTask.wrapperMethod(vertx, result -> {
        System.out.println("结果:" + result.result());
    });
});

class AsyncClass {
    private final int taskCount;

    public AsyncClass(int taskCount) {
        this.taskCount = taskCount;
    }

    public void wrapperMethod(Vertx vertx, Handler<AsyncResult<Integer>> resultHandler) {
        vertx.executeBlocking(future -> {
            blockingMethod();
            future.complete(taskCount); // 使用实例持有的触发时的count值
        }, resultHandler);
    }
}

额外建议

  1. 当用executeBlocking处理Event Loop触发的周期性任务时,一定要捕获触发时的变量快照,避免共享变量被后续修改覆盖
  2. 如果不需要任务串行执行,可以把executeBlocking的ordered参数设为false,让任务并行提交到Worker线程池,提升吞吐量(注意保证阻塞逻辑的线程安全性)
  3. 可通过vertx.createSharedWorkerExecutor创建自定义大小的Worker线程池,避免默认池被占满影响其他业务任务

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:00:46