1. 簡介
Tomcat繼承並重寫了JAVA原生的java.util.concurrent.ThreadPoolExecutor,增加了一些更有效率的方法,並且默認拒絕策略爲RejectedExecutionException。
2. 原理分析
2.1 實例化
與阿里巴巴JAVA規範中定義的線程池創建規範類似,Tomcat實例化線程池時,同樣限制了任務隊列的長度,maxQueueSize默認爲Integer.MAX_VALUE,但是可以通過修改配置xml注入新值。
另一方面,Tomcat同樣設置了線程池的核心線程數與最大線程數,默認爲25和200。
/**
* max number of threads
*/
protected int maxThreads = 200;
/**
* min number of threads
*/
protected int minSpareThreads = 25;
protected void startInternal() throws LifecycleException {
taskqueue = new TaskQueue(maxQueueSize);
TaskThreadFactory tf = new TaskThreadFactory(namePrefix,daemon,getThreadPriority());
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), maxIdleTime, TimeUnit.MILLISECONDS,taskqueue, tf);
executor.setThreadRenewalDelay(threadRenewalDelay);
if (prestartminSpareThreads) {
executor.prestartAllCoreThreads();
}
taskqueue.setParent(executor);
setState(LifecycleState.STARTING);
}
2.2 構造函數
Tomcat ThreadPoolExecutor構造函數,均調用了java.util.concurrent.ThreadPoolExecutor的prestartAllCoreThreads方法,創建線程池對象後,直接啓動所有核心線程,以提高系統效率。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
prestartAllCoreThreads();
}
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
prestartAllCoreThreads();
}
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, new RejectHandler());
prestartAllCoreThreads();
}
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new RejectHandler());
prestartAllCoreThreads();
}
/**
* Starts all core threads, causing them to idly wait for work. This
* overrides the default policy of starting core threads only when
* new tasks are executed.
*
* @return the number of threads started
*/
public int prestartAllCoreThreads() {
int n = 0;
while (addWorker(null, true))
++n;
return n;
}
2.3 execute
Tomcat ThreadPoolExecutor主要改動在於任務提交方法execute(Runnable command),修改後的邏輯如下:
- CAS修改submittedCount,submittedCount爲已提交且未結束的任務數量,包括任務隊列中的任務和執行中的任務
- 調用super.execute方法提交任務
- 捕獲RejectedExecutionException異常,嘗試將任務添加到任務隊列中
- 如果添加失敗,則拋出RejectedExecutionException
public void execute(Runnable command, long timeout, TimeUnit unit) {
// CAS submittedCount+1
submittedCount.incrementAndGet();
try {
// 提交任務
super.execute(command);
} catch (RejectedExecutionException rx) {
if (super.getQueue() instanceof TaskQueue) {
final TaskQueue queue = (TaskQueue)super.getQueue();
try {
// 入隊,如果隊列滿則阻塞,直到timeout
if (!queue.force(command, timeout, unit)) {
// 入隊失敗,則CAS submittedCount-1
submittedCount.decrementAndGet();
throw new RejectedExecutionException(sm.getString("threadPoolExecutor.queueFull"));
}
} catch (InterruptedException x) {
// 阻塞中被中斷,則CAS submittedCount-1
submittedCount.decrementAndGet();
throw new RejectedExecutionException(x);
}
} else {
submittedCount.decrementAndGet();
throw rx;
}
}
}
2.4 TaskQueue
TaskQueue繼承了LinkedBlockingQueue類,主要添加了兩個方法force(Runnable o)和force(Runnable o, long timeout, TimeUnit unit),根據線程池狀態決定是否調用隊列的offer方法。
public boolean force(Runnable o) {
if ( parent==null || parent.isShutdown() ) throw new RejectedExecutionException(sm.getString("taskQueue.notRunning"));
return super.offer(o); //forces the item onto the queue, to be used if the task is rejected
}
public boolean force(Runnable o, long timeout, TimeUnit unit) throws InterruptedException {
if ( parent==null || parent.isShutdown() ) throw new RejectedExecutionException(sm.getString("taskQueue.notRunning"));
return super.offer(o,timeout,unit); //forces the item onto the queue, to be used if the task is rejected
}
2.5 拒絕策略
當任務隊列滿時,父類的execute直接拋出RejectedExecutionException異常。
private static class RejectHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r,
java.util.concurrent.ThreadPoolExecutor executor) {
throw new RejectedExecutionException();
}
}
3. 總結
總得來說,Tomcat在原生的ThreadPoolExecutor的基礎上,做了小範圍的修改,部分提升了高併發下的性能,並減小了錯誤率。比較而言,Jetty自研的QTP與Java原生的線程池差別要大的多。