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

Quarkus启动时新线程执行任务触发ContextNotActiveException问题

Quarkus启动时异步填充缓存触发ContextNotActiveException的解决办法

问题场景

在Quarkus项目中,通过@Observes StartupEvent实现启动时填充缓存,因数据拉取耗时,手动创建新线程执行缓存填充方法,调用注入的SomeRestClient时触发ContextNotActiveException。核心代码如下:

public class CacheClass {

  private List<SomeObject> cacheData;

  @Inject
  SomeRestClient someRestClient;

  public void init(@Observes StartupEvent e) {
    // 手动创建线程执行缓存填充
    Thread thread = new Thread(() -> fetchDataForCache());
    thread.start();
    // 其他缓存填充线程
  }

  public void fetchDataForCache() {
    List<SomeObject> tmpData = someRestClient.fetchSomeData();
    // 数据验证与缓存写入逻辑
  }

  // Getter、Setter等其他代码
}

异常栈信息:

Exception in thread "Thread-68" javax.enterprise.context.ContextNotActiveException
at io.quarkus.arc.impl.ClientProxies.getDelegate(ClientProxies.java:46)
at someRestPackage.SomeRestClient_ClientProxy.arc$delegate(Unknown Source)
at someRestPackage.SomeRestClient_ClientProxy.findLatestEntry(Unknown Source)
at somePackage.CacheClass(CacheClass.java:15)
at somePackage.CacheClass_Subclass.fetchDataForCache$$superforward1(Unknown Source)
at somePackage.CacheClass_Subclass$$function$$3.apply(Unknown Source)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:51)
at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(Unknown Source)
at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
at somePackage.CacheClass_Subclass.fetchDataForCache(Unknown Source)
at somePackage.CacheClass$1.run(CacheClass.java:11)
at java.base/java.lang.Thread.run(Thread.java:829)

异常原因

Quarkus的CDI上下文(如ApplicationScoped、RequestScoped)不会自动传播到手动创建的线程中。注入的SomeRestClient是CDI代理对象,调用其方法时要求当前线程处于活跃的CDI上下文,而手动创建的线程未绑定任何CDI上下文,因此抛出ContextNotActiveException。

修复方案

方案1:使用@Async注解(简单易用)

借助Quarkus的异步API,通过@Async标记缓存填充方法,Quarkus会自动在后台线程执行并处理上下文传播。需确保项目引入quarkus-smallrye-async-api依赖。

修改后代码:

import io.smallrye.common.annotation.Async;

public class CacheClass {

  private List<SomeObject> cacheData;

  @Inject
  SomeRestClient someRestClient;

  public void init(@Observes StartupEvent e) {
    // 直接调用异步方法,Quarkus自动处理线程与上下文
    fetchDataForCache();
    // 其他缓存填充异步方法直接调用即可
  }

  @Async
  public void fetchDataForCache() {
    List<SomeObject> tmpData = someRestClient.fetchSomeData();
    // 数据验证与缓存写入逻辑
  }

  // Getter、Setter等其他代码
}

方案2:使用ManagedExecutor(灵活可控)

注入Quarkus提供的ManagedExecutor,将缓存填充任务提交到托管线程池,它会自动确保CDI上下文传播到任务线程。需引入quarkus-smallrye-context-propagation依赖(若未包含)。

修改后代码:

import jakarta.enterprise.concurrent.ManagedExecutor;

public class CacheClass {

  private List<SomeObject> cacheData;

  @Inject
  SomeRestClient someRestClient;
  
  @Inject
  ManagedExecutor managedExecutor;

  public void init(@Observes StartupEvent e) {
    // 提交缓存填充任务到托管线程池
    managedExecutor.submit(this::fetchDataForCache);
    // 提交其他缓存填充任务
    // managedExecutor.submit(this::fetchAnotherCacheData);
  }

  public void fetchDataForCache() {
    List<SomeObject> tmpData = someRestClient.fetchSomeData();
    // 数据验证与缓存写入逻辑
  }

  // Getter、Setter等其他代码
}

方案3:手动传播上下文(不推荐)

若必须手动创建线程,可通过Quarkus Arc API手动捕获并激活上下文,但该方式复杂易出错,仅作备选:

import io.quarkus.arc.Arc;
import io.quarkus.arc.InvocationContext;

public class CacheClass {

  private List<SomeObject> cacheData;

  @Inject
  SomeRestClient someRestClient;

  public void init(@Observes StartupEvent e) {
    // 捕获当前上下文
    InvocationContext context = Arc.current().getInvocationContext();
    Thread thread = new Thread(() -> {
      // 在新线程中激活上下文
      try (InvocationContext.ContextHandle handle = context.activate()) {
        fetchDataForCache();
      }
    });
    thread.start();
  }

  public void fetchDataForCache() {
    List<SomeObject> tmpData = someRestClient.fetchSomeData();
    // 数据验证与缓存写入逻辑
  }

  // Getter、Setter等其他代码
}

总结

优先使用Quarkus原生的异步机制(@Async或ManagedExecutor)处理启动时的异步缓存填充,避免手动创建线程,这类机制会自动处理CDI上下文传播,彻底规避ContextNotActiveException问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 17:24:17