Tomcat線程池的優化

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原生的線程池差別要大的多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章