【JUC】JDK1.8源碼分析之ThreadPoolExecutor(一)


一、前言

  JUC這部分還有線程池這一塊沒有分析,需要抓緊時間分析,下面開始ThreadPoolExecutor,其是線程池的基礎,分析完了這個類會簡化之后的分析,線程池可以解決兩個不同問題:由於減少了每個任務調用的開銷,它們通常可以在執行大量異步任務時提供增強的性能,並且還可以提供綁定和管理資源(包括執行任務集時使用的線程)的方法。下面開始分析。

二、ThreadPoolExecutor數據結構

   在ThreadPoolExecutor的內部,主要由BlockingQueue和AbstractQueuedSynchronizer對其提供支持,BlockingQueue接口有多種數據結構的實現,如LinkedBlockingQueueArrayBlockingQueue等,而AbstractQueuedSynchronizer在之前有過詳細的分析,有興趣的讀者可以參考。

三、ThreadPoolExecutor源碼分析

  3.1 類的繼承關系

public class ThreadPoolExecutor extends AbstractExecutorService {}

  說明:ThreadPoolExecutor繼承自AbstractExecutorService,AbstractExecuetorService提供了ExecutorService執行方法的默認實現。

  3.2 類的內部類

  ThreadPoolExecutor的核心內部類為Worker,其對資源進行了復用,減少創建線程的開銷,還有若干個策略類。內部類的類圖如下

  說明:可以看到Worker繼承了AQS抽象類並且實現了Runnable接口,其是ThreadPoolExecutor的核心內部類。而對於AbortPolicy,用於被拒絕任務的處理程序,它將拋出 RejectedExecutionException、CallerRunsPolicy,用於被拒絕任務的處理程序,它直接在 execute 方法的調用線程中運行被拒絕的任務;如果執行程序已關閉,則會丟棄該任務、DiscardPolicy,用於被拒絕任務的處理程序,默認情況下它將丟棄被拒絕的任務、DiscardOldestPolicy,用於被拒絕任務的處理程序,它放棄最舊的未處理請求,然后重試 execute;如果執行程序已關閉,則會丟棄該任務。這些都是拒絕任務提交時的所采用的不同策略。

  ① Worker類

  1. 類的繼承關系  

private final class Worker 
    extends AbstractQueuedSynchronizer 
    implements Runnable {}

   說明:Worker繼承了AQS抽象類,其重寫了AQS的一些方法,並且其也可作為一個Runnable對象,從而可以創建線程Thread。

  2. 類的屬性

    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        /**
         * This class will never be serialized, but we provide a
         * serialVersionUID to suppress a javac warning.
         */
        // 版本號
        private static final long serialVersionUID = 6138294804551838833L;

        /** Thread this worker is running in.  Null if factory fails. */
        // worker 所對應的線程
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        // worker所對應的第一個任務
        Runnable firstTask;
        /** Per-thread task counter */
        // 已完成任務數量
        volatile long completedTasks;
    }
View Code

  說明:Worker屬性中比較重要的屬性如下,Thread類型的thread屬性,用來封裝worker(因為worker為Runnable對象),表示一個線程;Runnable類型的firstTask,其表示該worker所包含的Runnable對象,即用戶自定義的Runnable對象,完成用戶自定義的邏輯的Runnable對象;volatile修飾的long類型的completedTasks,表示已完成的任務數量。

  3. 類的構造函數

        Worker(Runnable firstTask) {
            // 設置狀態為-1
            setState(-1); // inhibit interrupts until runWorker
            // 初始化第一個任務
            this.firstTask = firstTask;
            // 根據當前worker,初始化線程
            this.thread = getThreadFactory().newThread(this);
        }
View Code

  說明:用於構造一個worker對象,並設置AQS的state為-1,同時初始化了對應的域。

  4. 核心函數分析

// 重寫了Runnable的run方法
        public void run() {
            runWorker(this);
        }

        // Lock methods
        //
        // The value 0 represents the unlocked state.
        // The value 1 represents the locked state.
        // 是否被獨占,0代表未被獨占,1代表被獨占
        protected boolean isHeldExclusively() {
            return getState() != 0;
        }
        // 嘗試獲取
        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) { // 比較並設置狀態成功
                // 設置獨占線程
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        // 嘗試釋放
        protected boolean tryRelease(int unused) {
            // 設置獨占線程為null
            setExclusiveOwnerThread(null);
            // 設置狀態為0
            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(); }
        // 
        void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) { // AQS狀態大於等於0並且worker對應的線程不為null並且該線程沒有被中斷
                try {
                    // 中斷線程
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
View Code

  說明:Worker的函數主要是重寫了AQS的相應函數和重寫了Runnable的run函數,重寫的函數比較簡單,具體的可以參見AQS的分析,這里不再累贅。

  3.3 類的屬性  

public class ThreadPoolExecutor extends AbstractExecutorService {
    // 線程池的控制狀態(用來表示線程池的運行狀態(整形的高3位)和運行的worker數量(低29位))
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    // 29位的偏移量
    private static final int COUNT_BITS = Integer.SIZE - 3;
    // 最大容量(2^29 - 1)
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    // 線程運行狀態,總共有5個狀態,需要3位來表示(所以偏移量的29 = 32 - 3)
    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;
    // 阻塞隊列
    private final BlockingQueue<Runnable> workQueue;
    // 可重入鎖
    private final ReentrantLock mainLock = new ReentrantLock();
    // 存放工作線程集合
    private final HashSet<Worker> workers = new HashSet<Worker>();
    // 終止條件
    private final Condition termination = mainLock.newCondition();
    // 最大線程池容量
    private int largestPoolSize;
    // 已完成任務數量
    private long completedTaskCount;
    // 線程工廠
    private volatile ThreadFactory threadFactory;
    // 拒絕執行處理器
    private volatile RejectedExecutionHandler handler;
    // 線程等待運行時間
    private volatile long keepAliveTime;
    // 是否運行核心線程超時
    private volatile boolean allowCoreThreadTimeOut;
    // 核心池的大小
    private volatile int corePoolSize;
    // 最大線程池大小
    private volatile int maximumPoolSize;
    // 默認拒絕執行處理器
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();
    //
    private static final RuntimePermission shutdownPerm =
        new RuntimePermission("modifyThread");
}    
View Code

  說明:這里着重講解一下AtomicInteger類型的ctl屬性,ctl為線程池的控制狀態,用來表示線程池的運行狀態(整形的高3位)和運行的worker數量(低29位)),其中,線程池的運行狀態有如下幾種

    /**
    * RUNNING    :    接受新任務並且處理已經進入阻塞隊列的任務
    * SHUTDOWN    :    不接受新任務,但是處理已經進入阻塞隊列的任務
    * STOP        :    不接受新任務,不處理已經進入阻塞隊列的任務並且中斷正在運行的任務
    * TIDYING    :    所有的任務都已經終止,workerCount為0, 線程轉化為TIDYING狀態並且調用terminated鈎子函數
    * TERMINATED:    terminated鈎子函數已經運行完成
    **/
    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;

  說明:由於有5種狀態,最少需要3位表示,所以采用的AtomicInteger的高3位來表示,低29位用來表示worker的數量,即最多表示2^29 - 1。

  3.4 類的構造函數

  1. ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>)型構造函數

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
View Code

  說明:該構造函數用給定的初始參數和默認的線程工廠及被拒絕的執行處理程序創建新的 ThreadPoolExecutor。

   2. ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>, ThreadFactory)型構造函數  

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }
View Code

  說明:該構造函數用給定的初始參數和默認被拒絕的執行處理程序創建新的 ThreadPoolExecutor

   3. ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>, RejectedExecutionHandler)型構造函數

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }
View Code

  說明:該構造函數用給定的初始參數和默認的線程工廠創建新的 ThreadPoolExecutor

   4. ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>, ThreadFactory, RejectedExecutionHandler)型構造函數

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||                                                // 核心大小不能小於0
            maximumPoolSize <= 0 ||                                            // 線程池的初始最大容量不能小於0
            maximumPoolSize < corePoolSize ||                                // 初始最大容量不能小於核心大小
            keepAliveTime < 0)                                                // keepAliveTime不能小於0
            throw new IllegalArgumentException();                                
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        // 初始化相應的域
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
View Code

  說明:該構造函數用給定的初始參數創建新的 ThreadPoolExecutor,其他的構造函數都會調用到此構造函數。

  3.5 核心函數分析

  1. execute函數  

    public void execute(Runnable command) {
        if (command == null) // 命令為null,拋出異常
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        /*
        * 進行下面三步
        *
        * 1. 如果運行的線程小於corePoolSize,則嘗試使用用戶定義的Runnalbe對象創建一個新的線程
        *     調用addWorker函數會原子性的檢查runState和workCount,通過返回false來防止在不應
        *     該添加線程時添加了線程
        * 2. 如果一個任務能夠成功入隊列,在添加一個線城時仍需要進行雙重檢查(因為在前一次檢查后
        *     該線程死亡了),或者當進入到此方法時,線程池已經shutdown了,所以需要再次檢查狀態,
        *    若有必要,當停止時還需要回滾入隊列操作,或者當線程池沒有線程時需要創建一個新線程
        * 3. 如果無法入隊列,那么需要增加一個新線程,如果此操作失敗,那么就意味着線程池已經shut
        *     down或者已經飽和了,所以拒絕任務
        */
        // 獲取線程池控制狀態
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) { // worker數量小於corePoolSize
            if (addWorker(command, true)) // 添加worker
                // 成功則返回
                return;
            // 不成功則再次獲取線程池控制狀態
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) { // 線程池處於RUNNING狀態,將命令(用戶自定義的Runnable對象)添加進workQueue隊列
            // 再次檢查,獲取線程池控制狀態
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command)) // 線程池不處於RUNNING狀態,將命令從workQueue隊列中移除
                // 拒絕執行命令
                reject(command);
            else if (workerCountOf(recheck) == 0) // worker數量等於0
                // 添加worker
                addWorker(null, false);
        }
        else if (!addWorker(command, false)) // 添加worker失敗
            // 拒絕執行命令
            reject(command);
    }
View Code

   說明:當在客戶端調用submit時,之后會間接調用到execute函數,其在將來某個時間執行給定任務,此方法中並不會直接運行給定的任務。此方法中主要會調用到addWorker函數,其中,addWorker函數源碼如下  

    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) { // 外層無限循環
            // 獲取線程池控制狀態
            int c = ctl.get();
            // 獲取狀態
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&            // 狀態大於等於SHUTDOWN,初始的ctl為RUNNING,小於SHUTDOWN
                ! (rs == SHUTDOWN &&        // 狀態為SHUTDOWN
                   firstTask == null &&        // 第一個任務為null
                   ! workQueue.isEmpty()))     // worker隊列不為空
                // 返回
                return false;

            for (;;) {
                // worker數量
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||                                // worker數量大於等於最大容量
                    wc >= (core ? corePoolSize : maximumPoolSize))    // worker數量大於等於核心線程池大小或者最大線程池大小
                    return false;
                if (compareAndIncrementWorkerCount(c))                 // 比較並增加worker的數量
                    // 跳出外層循環
                    break retry;
                // 獲取線程池控制狀態
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs) // 此次的狀態與上次獲取的狀態不相同
                    // 跳過剩余部分,繼續循環
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        // worker開始標識
        boolean workerStarted = false;
        // worker被添加標識
        boolean workerAdded = false;
        // 
        Worker w = null;
        try {
            // 初始化worker
            w = new Worker(firstTask);
            // 獲取worker對應的線程
            final Thread t = w.thread;
            if (t != null) { // 線程不為null
                // 線程池鎖
                final ReentrantLock mainLock = this.mainLock;
                // 獲取鎖
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    // 線程池的運行狀態
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||                                    // 小於SHUTDOWN
                        (rs == SHUTDOWN && firstTask == null)) {            // 等於SHUTDOWN並且firstTask為null
                        if (t.isAlive()) // precheck that t is startable    // 線程剛添加進來,還未啟動就存活
                            // 拋出線程狀態異常
                            throw new IllegalThreadStateException();
                        // 將worker添加到worker集合
                        workers.add(w);
                        // 獲取worker集合的大小
                        int s = workers.size();
                        if (s > largestPoolSize) // 隊列大小大於largestPoolSize
                            // 重新設置largestPoolSize
                            largestPoolSize = s;
                        // 設置worker已被添加標識
                        workerAdded = true;
                    }
                } finally {
                    // 釋放鎖
                    mainLock.unlock();
                }
                if (workerAdded) { // worker被添加
                    // 開始執行worker的run方法
                    t.start();
                    // 設置worker已開始標識
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted) // worker沒有開始
                // 添加worker失敗
                addWorkerFailed(w);
        }
        return workerStarted;
    }
View Code

  說明:此函數可能會完成如下幾件任務

  ① 原子性的增加workerCount。

  ② 將用戶給定的任務封裝成為一個worker,並將此worker添加進workers集合中。

  ③ 啟動worker對應的線程,並啟動該線程,運行worker的run方法。

  ④ 回滾worker的創建動作,即將worker從workers集合中刪除,並原子性的減少workerCount。

  2. runWorker函數  

    final void runWorker(Worker w) {
        // 獲取當前線程
        Thread wt = Thread.currentThread();
        // 獲取w的firstTask
        Runnable task = w.firstTask;
        // 設置w的firstTask為null
        w.firstTask = null;
        // 釋放鎖(設置state為0,允許中斷)
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) { // 任務不為null或者阻塞隊列還存在任務
                // 獲取鎖
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||    // 線程池的運行狀態至少應該高於STOP
                     (Thread.interrupted() &&                // 線程被中斷
                      runStateAtLeast(ctl.get(), STOP))) &&    // 再次檢查,線程池的運行狀態至少應該高於STOP
                    !wt.isInterrupted())                    // wt線程(當前線程)沒有被中斷
                    wt.interrupt();                            // 中斷wt線程(當前線程)
                try {
                    // 在執行之前調用鈎子函數
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        // 運行給定的任務
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        // 執行完后調用鈎子函數
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    // 增加給worker完成的任務數量
                    w.completedTasks++;
                    // 釋放鎖
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            // 處理完成后,調用鈎子函數
            processWorkerExit(w, completedAbruptly);
        }
    }
View Code

  說明:此函數中會實際執行給定任務(即調用用戶重寫的run方法),並且當給定任務完成后,會繼續從阻塞隊列中取任務,直到阻塞隊列為空(即任務全部完成)。在執行給定任務時,會調用鈎子函數,利用鈎子函數可以完成用戶自定義的一些邏輯。在runWorker中會調用到getTask函數和processWorkerExit鈎子函數,其中,getTask函數源碼如下  

    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) { // 無限循環,確保操作成功
            // 獲取線程池控制狀態
            int c = ctl.get();
            // 運行的狀態
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { // 大於等於SHUTDOWN(表示調用了shutDown)並且(大於等於STOP(調用了shutDownNow)或者worker阻塞隊列為空)
                // 減少worker的數量
                decrementWorkerCount();
                // 返回null,不執行任務
                return null;
            }
            // 獲取worker數量
            int wc = workerCountOf(c);

            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; // 是否允許coreThread超時或者workerCount大於核心大小

            if ((wc > maximumPoolSize || (timed && timedOut))     // worker數量大於maximumPoolSize
                && (wc > 1 || workQueue.isEmpty())) {            // workerCount大於1或者worker阻塞隊列為空(在阻塞隊列不為空時,需要保證至少有一個wc)
                if (compareAndDecrementWorkerCount(c))            // 比較並減少workerCount
                    // 返回null,不執行任務,該worker會退出
                    return null;
                // 跳過剩余部分,繼續循環
                continue;
            }

            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :    // 等待指定時間
                    workQueue.take();                                        // 一直等待,直到有元素
                if (r != null)
                    return r;
                // 等待指定時間后,沒有獲取元素,則超時
                timedOut = true;
            } catch (InterruptedException retry) {
                // 拋出了被中斷異常,重試,沒有超時
                timedOut = false;
            }
        }
    }
View Code

  說明:此函數用於從workerQueue阻塞隊列中獲取Runnable對象,由於是阻塞隊列,所以支持有限時間等待(poll)和無限時間等待(take)。在該函數中還會響應shutDown和、shutDownNow函數的操作,若檢測到線程池處於SHUTDOWN或STOP狀態,則會返回null,而不再返回阻塞隊列中的Runnalbe對象。

  processWorkerExit函數是在worker退出時調用到的鈎子函數,而引起worker退出的主要因素如下

  ① 阻塞隊列已經為空,即沒有任務可以運行了。

  ② 調用了shutDown或shutDownNow函數

  processWorkerExit的源碼如下  

    private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // 如果被中斷,則需要減少workCount    // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();
        // 獲取可重入鎖
        final ReentrantLock mainLock = this.mainLock;
        // 獲取鎖
        mainLock.lock();
        try {
            // 將worker完成的任務添加到總的完成任務中
            completedTaskCount += w.completedTasks;
            // 從workers集合中移除該worker
            workers.remove(w);
        } finally {
            // 釋放鎖
            mainLock.unlock();
        }
        // 嘗試終止
        tryTerminate();
        // 獲取線程池控制狀態
        int c = ctl.get();
        if (runStateLessThan(c, STOP)) { // 小於STOP的運行狀態
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty()) // 允許核心超時並且workQueue阻塞隊列不為空
                    min = 1;
                if (workerCountOf(c) >= min) // workerCount大於等於min
                    // 直接返回
                    return; // replacement not needed
            }
            // 添加worker
            addWorker(null, false);
        }
    }
View Code

  說明:此函數會根據是否中斷了空閑線程來確定是否減少workerCount的值,並且將worker從workers集合中移除並且會嘗試終止線程池。

  3. shutdown函數  

    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 檢查shutdown權限
            checkShutdownAccess();
            // 設置線程池控制狀態為SHUTDOWN
            advanceRunState(SHUTDOWN);
            // 中斷空閑worker
            interruptIdleWorkers();
            // 調用shutdown鈎子函數
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        // 嘗試終止
        tryTerminate();
    }
View Code

  說明:此函數會按過去執行已提交任務的順序發起一個有序的關閉,但是不接受新任務。首先會檢查是否具有shutdown的權限,然后設置線程池的控制狀態為SHUTDOWN,之后中斷空閑的worker,最后嘗試終止線程池。
嘗試終止線程池tryTerminate的源碼如下

    final void tryTerminate() {
        for (;;) { // 無限循環,確保操作成功
            // 獲取線程池控制狀態
            int c = ctl.get();
            if (isRunning(c) ||                                            // 線程池的運行狀態為RUNNING
                runStateAtLeast(c, TIDYING) ||                            // 線程池的運行狀態最小要大於TIDYING
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))    // 線程池的運行狀態為SHUTDOWN並且workQueue隊列不為null
                // 不能終止,直接返回
                return;
            if (workerCountOf(c) != 0) { // 線程池正在運行的worker數量不為0    // Eligible to terminate
                // 僅僅中斷一個空閑的worker
                interruptIdleWorkers(ONLY_ONE);
                return;
            }
            // 獲取線程池的鎖
            final ReentrantLock mainLock = this.mainLock;
            // 獲取鎖
            mainLock.lock();
            try {
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { // 比較並設置線程池控制狀態為TIDYING
                    try {
                        // 終止,鈎子函數
                        terminated();
                    } finally {
                        // 設置線程池控制狀態為TERMINATED
                        ctl.set(ctlOf(TERMINATED, 0));
                        // 釋放在termination條件上等待的所有線程
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                // 釋放鎖
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }
View Code

  說明:如果線程池的狀態為SHUTDOWN並且線程池和阻塞隊列都為空或者狀態為STOP並且線程池為空,則將線程池控制狀態轉化為TERMINATED;否則,將中斷一個空閑的worker,其中,interruptIdleWorkers的源碼如下 

    private void interruptIdleWorkers(boolean onlyOne) {
        // 線程池的鎖
        final ReentrantLock mainLock = this.mainLock;
        // 獲取鎖
        mainLock.lock();
        try {
            for (Worker w : workers) { // 遍歷workers隊列
                // worker對應的線程
                Thread t = w.thread;
                if (!t.isInterrupted() && w.tryLock()) { // 線程未被中斷並且成功獲得鎖
                    try {
                        // 中斷線程
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        // 釋放鎖
                        w.unlock();
                    }
                }
                if (onlyOne) // 若只中斷一個,則跳出循環
                    break;
            }
        } finally {
            // 釋放鎖
            mainLock.unlock();
        }
    }
View Code

  說明:此函數將會中斷正在等待任務的空閑worker。

  shutdownNow函數與shutdown函數相似,shutdownNow會嘗試停止所有的活動執行任務、暫停等待任務的處理,並返回等待執行的任務列表,但是其會終止所有的worker,而並非空閑的worker。

  對於其他的函數,有興趣的讀者可以自行分析,下面通過一個示例來詳細講解ThreadPoolExecutor的內部工作機制。

四、示例

  通過上面的分析,對於一些重要的函數有了一個整體的認識,下面通過一個示例,看看這些函數之間是如何串聯起來的,並且分析分析ThreadPoolExecutor的工作機制。

package com.hust.grid.leesf.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class FixedThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService es = Executors.newFixedThreadPool(2);
        MyRunnable mr1 = new MyRunnable(10, "mr1");
        MyRunnable mr2 = new MyRunnable(5, "mr2");
        MyRunnable mr3 = new MyRunnable(10, "mr3");
        
        es.submit(mr1);
        es.submit(mr2);
        es.submit(mr3);
        
        es.shutdown();
    }
    
    static class MyRunnable implements Runnable {
        private int count;
        private String name;
        
        public MyRunnable(int count, String name) {
            this.count = count;
            this.name = name;
        }
        
        public void run() {
            for (int i = 0; i < count; i++) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(name);
            }
        }
    }
}
View Code

  運行結果(某一次)

mr1
mr2
mr2
mr1
mr2
mr1
mr1
mr2
mr2
mr1
mr3
mr1
mr3
mr1
mr3
mr1
mr1
mr3
mr3
mr1
mr3
mr3
mr3
mr3
mr3
View Code

  說明:在程序中,使用了一個FixedThreadPool線程池(即corePoolSize與maximumPoolSize相等,且為2),之后在線程池提交了3個線程(Runnalbe對象),之后調用了shutdown來關閉線程池。

  ① 執行es.submit(mr1),其主要的函數調用如下

  說明:在調用了es.submit(mr1)后,最終線程池中會新建一個worker,並且此時workQueue阻塞隊列為空(沒有元素),並且值得注意的是,在runWorker函數中,有一個while循環,當某個任務完成后,會從workQueue阻塞隊列中取下一個任務。

  ② 執行es.submit(mr2),其主要的函數調用與執行es.submit(mr1)相同,但是此時的線程池狀態有所不同,其狀態如下

  說明:此時,線程池會有兩個worker,兩個worker會分別封裝mr1和mr2,並且workQueue阻塞隊列還是為空(沒有元素)。

  ③ 執行es.submit(mr3),其主要的函數調用如下

  說明:此時,由於線程池的worker的數量已經達到了corePoolSize大小,所以,此時會將mr3放入到workQueue阻塞隊列中,此時,線程池還是只有兩個worker,並且阻塞隊列已經存在一個mr3元素。

  ④ mr2定義的邏輯運行完成,則會從workQueue中取下一個任務(mr3)。主要的函數調用如下(從runWorker開始)

  說明:此時,會運行用戶再mr3中自定義的邏輯。此時,線程池中還是有兩個worker,並且workQueue的大小為0,沒有元素。

  ⑤ mr1定義的邏輯運行完成,則還是會從workQueue中取下一個任務(null)。主要的函數調用如下(從runWorker開始)

  說明:此時,由於是阻塞隊列,並且隊列中沒有元素,所以調用take會使當前線程(worker對應的Thread)被阻塞。

  ⑥ mr3定義的邏輯運行完成,其過程和mr1完成時相同,會使另外一個worker對應的Thread被阻塞。

  ⑦ 執行es.shutdown,則主要的函數調用如下

  說明:在執行shutdown后,會中斷兩個worker對應的Thread線程。由於中斷了worker對應的Thread線程,則之前由於take操作(響應中斷)而阻塞也會被中斷。

  ⑧ 其中一個worker對應的線程響應中斷,從getTask函數開始(因為在getTask中被阻塞)。

  說明:此時,在getTask函數中,會將workerCount的值減一,並且返回null。接着在runWorker函數中退出while循環,並進入processWorkerExit函數進行worker退出線程池的處理,之后會再次調用addWorker,但是此時,不會添加成功。此時,線程池只有一個worker,並且workQueue的大小還是為0。

  ⑨ 另外一個worker對應的線程響應中斷,從getTask函數開始(因為在getTask中被阻塞)。與上一個worker的處理過程相同,不再累贅。線程池的狀態如下

  說明:之后整個程序就運行結束了,最后的狀態為workQueue阻塞隊列大小為0,線程池沒有worker,workerCount為0。

  最后,給出ThreadPoolExecutor的示意圖

  說明:用戶自定義的任務會進入阻塞隊列或者直接進入線程池(進入線程池后,新建線程直接運行),worker會從阻塞隊列中不斷的取任務,直到阻塞隊列中沒有任務。

  關於ThreadPoolExecutor還有如下幾點需要注意的

  ① corePoolSize,表示核心大小,如果運行的線程少於 corePoolSize,則創建新線程來處理請求,即使其他輔助線程是空閑的。

  ② maxPoolSzie,表示阻塞隊列的大小,如果運行的線程多於 corePoolSize 而少於 maximumPoolSize,則僅當阻塞隊列滿時才創建新線程。如果設置的 corePoolSize 和 maximumPoolSize 相同,則創建了固定大小的線程池(如本例的FixThreadPool)。如果將 maximumPoolSize 設置為基本的無界值(如 Integer.MAX_VALUE),則允許池適應任意數量的並發任務。

  ③ largestPoolSize,表示曾經同時存在在線程池的worker的大小,為workers集合(維護worker)的大小。

  ④ 關於shutdown函數和shutdownNow函數的區別,shutdown會設置線程池的運行狀態為SHUTDOWN,並且中斷所有空閑的worker,由於worker運行時會進行相應的檢查,所以之后會退出線程池,並且其會繼續運行之前提交到阻塞隊列中的任務,不再接受新任務。而shutdownNow則會設置線程池的運行狀態為STOP,並且中斷所有的線程(包括空閑和正在運行的線程),在阻塞隊列中的任務將不會被運行,並且會將其轉化為List<Runnable>返回給調用者,也不再接受新任務,其不會停止用戶任務(只是發出了中斷信號),若需要停止,需要用戶自定義停止邏輯。

五、總結

  ThreadPoolExecutor是線程池框架的一個核心類,通過對ThreadPoolExecutor的分析,可以知道其對資源進行了復用,並非無限制的創建線程,可以有效的減少線程創建和切換的開銷,關於ThreadPoolExecutor的源碼就分析到這里,有疑問的讀者歡迎交流,謝謝各位園友的觀看~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM