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

基于Weld CDI的Web应用循环死锁问题排查求助

这问题我之前在Weld CDI环境里碰到过类似的,咱们一步步拆解来看:

死锁根源分析

从你给出的线程栈和Bean定义来看,死锁大概率出在Weld对Scoped Bean初始化的锁竞争上。结合场景推测下触发链:
同一个用户会话下,两个并发请求分别触发了PointOfSalesBean(ConversationScoped)和GeoManager(SessionScoped)的初始化:

  1. 线程1持有PointOfSalesBean的初始化锁,随后尝试获取KernelService(SessionScoped)的访问/初始化锁,但这个锁被线程2持有;
  2. 线程2持有GeoManager的初始化锁,而GeoManager的@PostConstruct直接调用了KernelService的方法,在这个过程中,Weld内部可能因为上下文依赖(比如SessionScoped访问时需要确认当前Conversation上下文),尝试获取线程1持有的PointOfSalesBean锁;
  3. 最终形成线程1持锁A等锁B,线程2持锁B等锁A的循环等待,触发死锁。
针对性解决方案

1. 延迟PostConstruct中的业务调用

GeoManager的@PostConstruct直接调用KernelService的方法,会在Bean初始化阶段就触发其他Scoped Bean的访问,放大锁竞争概率。改成延迟初始化:

@Inject KernelService service;
private List<Province> provinces;

// 移除原@PostConstruct的init方法
public OptionLabel getNationByCod(String cod) {
    // 第一次调用业务方法时再初始化
    if (provinces == null) {
        provinces = service.getProvinces();
    }
    // 原业务逻辑代码
}

2. 提前初始化SessionScoped核心Bean

既然KernelService是两个Bean都依赖的SessionScoped实例,可以在会话创建时就提前初始化它,避免并发请求同时触发初始化:

@SessionScoped
public class SessionInitializer implements Serializable {
    @Inject KernelService kernelService;

    @PostConstruct
    public void initSessionBeans() {
        // 随便调用一个无业务影响的方法,触发KernelService初始化
        kernelService.toString();
    }
}

这样后续请求进来时,KernelService已经存在,不会再触发初始化锁的竞争。

3. 升级Weld版本

如果你的应用使用的是Weld 2.x及更早的旧版本,建议升级到对应Java EE/Jakarta EE版本的最新稳定版(比如Weld 3.x适配Java EE 8,Weld 4.x适配Jakarta EE 9+)——旧版本Weld在Scoped上下文并发初始化时存在已知的死锁bug,升级后大概率能解决问题。

4. 验证锁竞争路径(可选)

如果以上方案没解决,建议开启Weld的DEBUG日志(设置org.jboss.weld日志级别为DEBUG),查看Bean初始化的完整调用链,确认两个线程到底是在获取哪个Bean的锁时卡住,进一步定位触发点。

内容的提问来源于stack exchange,提问作者500 Server error

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:45:09