项目需要在启动后死循环处理队列中的任务,具体的处理方法是异步的。之前的做法是在子线程中执行@Async
方法,一直有两个奇怪的现象:
- 会出现明明线程池里还有线程,
@Async
方法却不执行了。 - 配置的线程数总比真实使用的多一个。
针对第一个现象:
定位到是因为线程池的拒绝策略导致的, 原来的配置是CallerRunsPolicy.
JDK 提供了四种内置拒绝策略,我们要理解并记住,有如下的四种:
1、DiscardPolicy: 默默丢弃无法处理的任务,不予任何处理
2、DiscardOldestPolicy: 丢弃队列中最老的任务, 尝试再次提交当前任务
3、AbortPolicy: 直接抛异常,阻止系统正常工作。
4、CallerRunsPolicy: 将任务分给调用线程来执行,运行当前被丢弃的任务,这样做不会真的丢弃任务,但是提交的线程性能有可能急剧下降。
将拒绝策略改为默认的即可.
针对第二个问题:
经过深入调查发现@Async
方法被调用时会取线程执行器,通过子线程完成异步调用。如果没有线程执行器则直接调用getExecutorQualifier()
获取,所以如果是在子线程中调用的@Async
方法,会取子线程所在的线程池中的线程。所以此时无论是ThreadPoolTaskExecutor
、Executor
结果都会导致配置的最大线程数少了一个。
所以,不要在子线程中调用@Async方法
!
详细代码可以跟一下AsyncExecutionAspectSupport
这个类里的determineAsyncExecutor()
搞清楚原理解决就很容易了,将死循环换到ApplicationListener
中,在项目启动完成后执行,不再使用子线程即可解决。