@Async 到底用的是什么线程池?异常去哪了?ThreadLocal、MDC 能不能带过去?线程池满了会怎样?这篇在 Spring Framework 7.0.9 和 Spring Boot 4.0.8 上逐项验证。
纯 Spring,@EnableAsync 没有配置执行器:100 次 @Async 调用(每次 30 ms)用了 100 个不同的线程,线程名举例 [SimpleAsyncTaskExecutor-1, SimpleAsyncTaskExecutor-10] Spring Boot(@EnableAutoConfiguration + @EnableAsync,没有配执行器):100 次调用用了 8 个线程 [task-1, task-2, task-3, task-4, task-5, task-6, task-7, task-8];容器里的 TaskExecutor Bean:[applicationTaskExecutor] 配了 ThreadPoolTaskExecutor(核心 4):100 次调用用了 4 个线程 [pool-1, pool-2, pool-3, pool-4];完成 100 个
纯 Spring 每次新建线程,Boot 配了带 8 个核心线程的 applicationTaskExecutor。
(a)默认:
LOG ERROR SimpleAsyncUncaughtExceptionHandler: Unexpected exception occurred invoking async method: public void demo.Main$Work.boomVoid()
boomFuture() 返回的 Future:get() 抛 ExecutionException ← IllegalStateException: Future 方法里的异常
returnsString()(@Async 但返回 String)→ IllegalArgumentException: Invalid return type for async method (only Future and void supported): class java.lang.String
(b)自定义处理器:
自定义 AsyncUncaughtExceptionHandler 收到:void 方法里的异常,方法 boomVoid
boomFuture() 返回的 Future:get() 抛 ExecutionException ← IllegalStateException: Future 方法里的异常
returnsString()(@Async 但返回 String)→ IllegalArgumentException: Invalid return type for async method (only Future and void supported): class java.lang.Stringvoid 方法的异常默认只记日志,Future 方法的异常放进 Future,返回别的类型直接抛异常。
没有 TaskDecorator:提交线程 ThreadLocal = 请求-42;异步线程里读到 → 来自 pool-1,ThreadLocal=null
加了 TaskDecorator:提交线程 ThreadLocal = 请求-42;异步线程里读到 → 来自 deco-1,ThreadLocal=请求-42ThreadLocal 不跨线程,用 TaskDecorator 传递并记得清理。
核心 2、最大 2、队列 2、默认拒绝策略,提交 10 个各 300 ms 的任务:提交本身花了 3 ms;被拒绝 6 个 [TaskRejectedException];最终完成 4 个;执行过的线程 [sat-1, sat-2]
核心 2、最大 2、队列 2、CallerRunsPolicy,提交 10 个各 300 ms 的任务:提交本身花了 915 ms;被拒绝 0 个 [];最终完成 10 个;执行过的线程 [main, sat-1, sat-2]默认抛 TaskRejectedException,CallerRunsPolicy 让调用者自己执行。
从外部调用 @Async 方法 who():执行线程 [pool-1];callInternal() 里调 this.fire(0) 的调用者线程 main(内部调用不异步,见自调用篇)
this 调用 @Async 方法是同步的。