簡單分析ThreadPoolExecutor實現的1.6版和1.7版之間的不同

ThreadPoolExecutor,簡單來說就是一個線程池的Java實現,通過不同的參數可以達到不同的線程維護策略,在構造函數裏,提供以下參數:

1. corePoolSize。核心線程數。永久保留在線程池的線程數,即使處於空閒也不會被回收。
2. maximumPoolSize。最大線程數。線程池併發線程數超過此值,新提交任務會被放入阻塞隊列中等待調度。
3. keepAliveTime。線程池併發線程數超過核心線程數時,如果線程空閒超過時間超過此值就會被回收。
4. workQueue。阻塞隊列,當然提交的任務沒有足夠線程調度時,任務就被加入到這個隊列中。
5. threadFactory。工廠類創建線程
6. handler。線程數和隊列達到限制後,就會調用handler處理,默認拋出異常。

對於不同的需求,如大量的耗時短任務或者小量的任務之間有關聯等可以不同的參數。其具體工作原理如下圖:



從最右邊的fetch task可以看到,其核心原理就是一個簡單的生產-消費者模式,即用戶通過execute方法,往任務隊列添加任務,然後每條線程各自從隊列裏獲取任務執行。

經過查看源碼,發現了1.7版和1.6版本的ThreadPoolExecutor,雖然接口對外完全一致,但內部的代碼經過了大量的修改,總體上來說,1.7版的ThreadPoolExecutor實現比之前的JDK更加高效,代碼沒有1.6的實現那麼清晰。具體變化如下。

一、線程池狀態變量以及線程數合成一個AtomicInteger

      由於線程池要實現對狀態的維護以及和線程池內部運行中的線程數目,因此,1.6的實現裏創建了兩個volatile變量,runState、poolSize,並且每次更改這兩個變量的同時,必須要加鎖保護。

    volatile int runState;
    static final int RUNNING    = 0;
    static final int SHUTDOWN   = 1;
    static final int STOP       = 2;
    static final int TERMINATED = 3;
    /**
     * Current pool size, updated only while holding mainLock but
     * volatile to allow concurrent readability even during updates.
     */
    private volatile int   poolSize;

對於1.7,把這兩個變量合成一個AtomicInteger的原子整型變量,這樣可以達到了CAS旋轉鎖,去除必須要加lock的做法。

   private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }


    /**
     * Attempt to CAS-increment the workerCount field of ctl.
     */
    private boolean compareAndIncrementWorkerCount(int expect) {
        return ctl.compareAndSet(expect, expect + 1);
    }

    /**
     * Attempt to CAS-decrement the workerCount field of ctl.
     */
    private boolean compareAndDecrementWorkerCount(int expect) {
        return ctl.compareAndSet(expect, expect - 1);
    }

    /**
     * Decrements the workerCount field of ctl. This is called only on
     * abrupt termination of a thread (see processWorkerExit). Other
     * decrements are performed within getTask.
     */
    private void decrementWorkerCount() {
        do {} while (! compareAndDecrementWorkerCount(ctl.get()));
    }

由於這個改進,在1.7實現的代碼裏大量的關於這兩個變量的更改都變爲了旋轉鎖,這樣可以更好的提高線程池的性能。

二、Worker類的實現改進

Worker類在ThreadPoolExecutor內部是作爲一個工作線程的封裝類。由於每個工作線程可能在執行任務或者在等待着新的任務到來(也就是空閒線程),因此等調用setCorePoolSize等方法需要對空閒線程進行interrupt時,就必須要利用lock來對那些正在執行任務的鎖進行保護。

在1.6的實現中,Worker類只是單純實現Runnable接口,並且添加一個內部私有成員runLock的可重入鎖進行保護。

    private final class Worker implements Runnable {
        /**
         * The runLock is acquired and released surrounding each task
         * execution. It mainly protects against interrupts that are
         * intended to cancel the worker thread from instead
         * interrupting the task being run.
         */
        private final ReentrantLock runLock = new ReentrantLock();
	......
}


這樣在每次要interrupt空閒任務的線程的時候,要對當前線程進行判斷。

        /**
         * Interrupts thread if not running a task.
         */
        void interruptIfIdle() {
            final ReentrantLock runLock = this.runLock;
            if (runLock.tryLock()) {
                try {
		    if (thread != Thread.currentThread())
			thread.interrupt();
                } finally {
                    runLock.unlock();
                }
            }
        }

而在1.7的實現裏,對這個進行了簡單的優化,就是直接把Worker類繼承自AbstractQueuedSynchronizer類,然後簡單實現了一個不可重入的互斥鎖,這樣就不用每次都判斷。

  private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
	......

        // Lock methods
        //
        // The value 0 represents the unlocked state.
        // The value 1 represents the locked state.

        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        public void lock()        { acquire(1); }
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }
    }


對於AbstractQueuedSynchronizer類,這個是Java裏實現各種同步機制的基礎類,其原理是內部實現了一個簡單的線程鏈表,然後各種旋轉CAS來更改等待狀態,可重入鎖ReentrantLock也是繼承自這個類並實現同一線程記錄上鎖數的邏輯。

說回正題,這樣,在interrupt空閒線程時,可以不用判斷是否當前線程了。

    private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers) {
                Thread t = w.thread;
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

    Worker繼承AbstractQueuedSynchronizer。這樣也是避免當中斷worker線程等待任務執行,但錯誤地中斷了執行中的任務的中斷異常。主要是在方法interruptIdleWorkers裏:

    private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers) {
                Thread t = w.thread;
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

    該方法目的是爲了中斷空閒線程。首先獲取mainLock,保證workers集合穩定不變,然後tryLock嘗試獲得線程的鎖。由於worker線程在執行任務前必須獲得鎖,執行完成後才釋放鎖,這樣可以確保獲取鎖的worker線程肯定不是在執行任務中,因此可以確保只打斷空閒的線程。

    其實1.6和1.7的ThreadPoolExecutor的實現還是有比較大的不同地方,儘管整個流程大體一致,但一些細微的地方1.7版還是做了優化的,目的當然是爲了更好地提高性能,增加線程池的吞吐量。以上兩點不同之處,僅作爲本人的一點了解,旨在希望各位能夠好好了解其中的優化,希望能夠達到拋磚引玉的目的。



發佈了36 篇原創文章 · 獲贊 4 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章