SynchronousQueue的不恰当使用,瞬时过载导致线程池任务被拒绝
设计实现是要求在服务启动时,创建一批常驻线程,从阻塞队列里取元素执行业务逻辑。为此我刚开始使用的代码如下:
使用CommandLineRunner在服务启动时创建好线程
public class ConflictFactory implements CommandLineRunner{
private static final int CD_EXECUTOR_THREAD = Runtime.getRuntime().availableProcessors();
private static final ThreadPoolExecutor CD_EXECUTOR = new ThreadPoolExecutor(
CD_EXECUTOR_THREAD,
CD_EXECUTOR_THREAD,
0L,
TimeUnit.MILLISECONDS,
new SynchronousQueue<>(),
new NamedThreadFactory("CD-", false),
new ThreadPoolExecutor.AbortPolicy());
static {
CD_EXECUTOR.prestartAllCoreThreads();
}
@Override
public void run(String... args) throws Exception {
for (int i = 0; i < CD_EXECUTOR_THREAD; i++) {
CD_EXECUTOR.execute(() -> {
while (true) {
try {
node = singleDomainQueue.take();
、、、、、、
} catch (Exception e) {
、、、、、、
}
}
});
}
}
}
运行时偶发报错,提示任务被拒绝
创建的任务数量=核心线程数量为什么 任务还会被拒绝呢?
虽然使用了prestartAllCoreThreads()预先创建线程,但是当任务提交时,线程还未全部创建完毕,active threads = 1,还未达到CD_EXECUTOR_THREAD个。由于SynchronousQueue的特性 :
- 不会存储任务,必须由 某个线程立即消费,否则任务会被拒绝。
- 线程的创建、任务提交、任务执行 必须完全同步,否则任务会失败
瞬时过载导致任务被拒绝。
还是老老实实换成了LinkedBlockingQueue,暂存一下任务,等线程逐步创建完成再执行。
private static final ThreadPoolExecutor CD_EXECUTOR = new ThreadPoolExecutor(
CD_EXECUTOR_THREAD,
CD_EXECUTOR_THREAD,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(CD_EXECUTOR_THREAD + 3),
new NamedThreadFactory("CD-", false),
new ThreadPoolExecutor.AbortPolicy());
SynchronousQueue只适合任务数量<最大线程数量,并且线程已经是就绪状态的场景。高吞吐、低延迟,但适用于短时任务,线程池需要足够的线程数,否则任务会被拒绝