tomcat的线程池优化了几个方面
-
#offer
- 利用未销毁的空闲队列,利用tomcat的submitCount 与 线程池的 poolSize。
- 添加阻塞队列之前,先使用最大线程,最大程度让线程都工作。特别是队列为无界时,效果明显。
-
#execute,线程执行时,增加强制重试机制,最大程度保证可以执行。
-
#poll,执行poll方法时,先判断容器状态,如果容器停止了则退出getTask的循环。
-
tomcat线程池创建时,启动全部核心线程。
TaskQueue
Tomcat的阻塞队列
重写#offer方法
当一个线程池需要调用阻塞队列的 offer 的时候,说明线程池的核心线程数已经被占满了
/*
* 1. 利用未销毁的空闲线程
* 2. 如果是无界队列,可以重复利用最大线程数
*/
public boolean offer(Runnable o) {
//we can't do any checks
if (parent==null) return super.offer(o);
// 当线程池的线程(核心 + 非核心) = 最大线程时,丢到阻塞队列
if (parent.getPoolSize() == parent.getMaximumPoolSize()) return super.offer(o);
// 已提交的线程数 <= 核心线程。之前创建的临时线程还没有被回收,这个时候直接把线程加入到队里里面,自然就会被空闲的临时线程消费掉了
if (parent.getSubmittedCount()<=(parent.getPoolSize())) return super.offer(o);
/**
* 提交的线程数已经大于核心线程数了,且没有空闲线程。所以返回一个 false 标明,表示任务添加到阻塞队列失败。
* 线程池就会认为阻塞队列已经无法继续添加任务到队列中了,根据默认线程池的工作逻辑,线程池就会创建新的线程直到最大线程数。
*
* 如果阻塞队列是无界的,任务会无限的添加到无界的阻塞队列中,线程池就无法利用核心线程数和最大线程数之间的线程数了
*
* 目的:线程池即使核心线程数满了以后,且使用无界队列的时候,线程池依然有机会创建新的线程,直到达到线程池的最大线程数。
*/
if (parent.getPoolSize()<parent.getMaximumPoolSize()) return false;
//if we reached here, we need to add it to the queue
return super.offer(o);
}
submittedCount:Tomcat的自定义ThreadExecutor还能看到已经提交了的任务给线程池的总数,也就是submittedCount;
如:10个线程执行了#execute,可能只有8个正在执行,有2个可能执行了Reject,8就是也就是submittedCount
重写#poll方法
如果应用已经停止了,那么getTask也不需要一直自旋了,可以中止了
重写#execute方法
当遇到拒绝异常时,调用TaskQueue#force,”强行”的尝试向阻塞队列中添加任务。
创建线程池时,将启动全部核心线程
tomcat启动时会调用ThreadPoolExecutor#prestartAllCoreThreads,启动全部核心线程