EventBus默认同步,异步需用AsyncEventBus并传入自定义线程池;事件类型必须精确匹配,否则静默丢失;并发需加@AllowConcurrentEvents;注册/注销须成对防止内存泄漏和重复触发。

EventBus 默认是同步的,异步必须显式用 AsyncEventBus
很多人一上来就 new EventBus(),发事件后发现主线程卡住、监听方法串行阻塞、超时异常堆满日志——根本原因是 EventBus 默认使用 MoreExecutors.directExecutor(),所有 @Subscribe 方法都在发布线程里直接调用。真正需要异步分发,必须用 AsyncEventBus,且必须传入自定义线程池:
ExecutorService executor = Executors.newFixedThreadPool(4); AsyncEventBus asyncBus = new AsyncEventBus(executor);不传线程池会 fallback 到
MoreExecutors.newDirectExecutorService(),效果和 EventBus 一样。
@Subscribe 方法签名不匹配会导致事件静默丢失
Guava 对事件类型匹配极其严格:监听方法参数类型必须与 post() 发送的对象**精确一致**(不支持向上转型或泛型擦除后匹配)。比如你 post(new OrderCreatedEvent()),但监听方法写成 void onEvent(Event event) 或 void onEvent(Object event),该方法完全不会被调用,也不会报错,事件变成“死亡事件”(DeadEvent)。正确写法只有一种:
@Subscribe
public void handleOrderCreated(OrderCreatedEvent event) { ... }注意:方法不能是 private,不能是 static,参数有且仅有一个,且类型完全匹配。
并发执行需加 @AllowConcurrentEvents,否则默认串行
即使用了 AsyncEventBus,如果监听方法没加 @AllowConcurrentEvents 注解,Guava 仍会把多个同类型事件排队串行执行(包装成 SynchronizedSubscriber)。这在高吞吐场景下会成为瓶颈。要真正并行处理,必须显式标注:
@Subscribe
@AllowConcurrentEvents
public void handlePaymentCompleted(PaymentCompletedEvent event) { ... }同时确保方法体内部无共享可变状态,否则需自行加锁或改用线程安全结构。
注册/注销必须成对,否则内存泄漏+重复触发
eventBus.register(listener) 是把 listener 实例及其所有 @Subscribe 方法注册进 SubscriberRegistry 的 map 中;如果不调用 unregister(),listener 持有引用无法 GC,尤其在 Spring 管理的 prototype bean 或 Web 请求级对象中极易泄漏。更隐蔽的问题是:同一 listener 被重复 register 多次,对应事件就会触发多次——调试时看到日志打印两遍却找不到原因,往往就是这里。建议在 listener 生命周期结束处(如 Spring @PreDestroy)强制 unregister,或用 try-finally 包裹。
AsyncEventBus + 真实线程池,二是事件类型匹配和生命周期管理稍有松动,整个机制就失效得悄无声息。

















