深入分析線程池的實現原理

點擊上方“zhisheng”,選擇“設爲星標”

後臺回覆”666“獲取公衆號專屬資料

作者:指尖上的榴蓮(本文來自作者投稿)

www.jianshu.com/p/704a6c5d337c

一.概述

線程池,顧名思義就是存放線程的池子,池子裏存放了很多可以複用的線程。

如果不用類似線程池的容器,每當我們需要執行用戶任務的時候都去創建新的線程,任務執行完之後線程就被回收了,這樣頻繁地創建和銷燬線程會浪費大量的系統資源。

因此,線程池通過線程複用機制,並對線程進行統一管理,具有以下優點:

  • 降低系統資源消耗。通過複用已存在的線程,降低線程創建和銷燬造成的消耗;

  • 提高響應速度。當有任務到達時,無需等待新線程的創建便能立即執行;

  • 提高線程的可管理性。線程是稀缺資源,如果無限制的創建,不僅會消耗大量系統資源,還會降低系統的穩定性,使用線程池可以進行對線程進行統一的分配、調優和監控。

ThreadPoolExecutor是線程池框架的一個核心類,本文通過對ThreadPoolExecutor源碼的分析(基於JDK 1.8),來深入分析線程池的實現原理。

二.ThreadPoolExecutor類的屬性

先從ThreadPoolExecutor類中的字段開始:

// 線程池的控制狀態,用高3位來表示線程池的運行狀態,低29位來表示線程池中工作線程的數量
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    //值爲29,用來表示偏移量
    private static final int COUNT_BITS = Integer.SIZE - 3;
    //線程池的最大容量,其值的二進制爲:00011111111111111111111111111111(29個1)
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // 線程池的運行狀態,總共有5個狀態,用高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;

    //核心池大小,若allowCoreThreadTimeOut被設置,核心線程全部空閒超時被回收的情況下會爲0
    private volatile int corePoolSize;

    //最大池大小,不得超過CAPACITY
    private volatile int maximumPoolSize;

    //默認的任務拒絕策略
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();

    private static final RuntimePermission shutdownPerm =
        new RuntimePermission("modifyThread");

    private final AccessControlContext acc;

在ThreadPoolExecutor類的這些屬性中,線程池狀態是控制線程池生命週期至關重要的屬性,這裏就以線程池狀態爲出發點進行研究。

通過上面的源碼可知,線程池的運行狀態總共有5種,其值和含義分別如下:

  • RUNNING:  高3位爲111,接受新任務並處理阻塞隊列中的任務

  • SHUTDOWN: 高3位爲000,不接受新任務但會處理阻塞隊列中的任務

  • STOP:高3位爲001,不會接受新任務,也不會處理阻塞隊列中的任務,並且中斷正在運行的任務

  • TIDYING:  高3位爲010,所有任務都已終止,工作線程數量爲0,線程池將轉化到TIDYING狀態,即將要執行terminated()鉤子方法

  • TERMINATED: 高3位爲011,terminated()方法已經執行結束

然而,線程池中並沒有使用單獨的變量來表示線程池的運行狀態,而是使用一個AtomicInteger類型的變量ctl來表示線程池的控制狀態,其將線程池運行狀態與工作線程的數量打包在一個整型中,用高3位來表示線程池的運行狀態,低29位來表示線程池中工作線程的數量,對ctl的操作主要參考以下幾個函數:

// 通過與的方式,獲取ctl的高3位,也就是線程池的運行狀態
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    //通過與的方式,獲取ctl的低29位,也就是線程池中工作線程的數量
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    //通過或的方式,將線程池狀態和線程池中工作線程的數量打包成ctl
    private static int ctlOf(int rs, int wc) { return rs | wc; }
    //SHUTDOWN狀態的值是0,比它大的均是線程池停止或清理狀態,比它小的是運行狀態
    private static boolean isRunning(int c) {
        return c < SHUTDOWN;
    }

接下來,我們看一下線程池狀態的所有轉換情況,如下:

  • RUNNING -> SHUTDOWN:調用shutdown(),可能在finalize()中隱式調用

  • (RUNNING or SHUTDOWN) -> STOP:調用shutdownNow()

  • SHUTDOWN -> TIDYING:當緩存隊列和線程池都爲空時

  • STOP -> TIDYING:當線程池爲空時

  • TIDYING -> TERMINATED:當terminated()方法執行結束時

通常情況下,線程池有如下兩種狀態轉換流程:

  1. RUNNING -> SHUTDOWN -> TIDYING -> TERMINATED

  2. RUNNING -> STOP -> TIDYING -> TERMINATED


三.ThreadPoolExecutor類的構造方法

通常情況下,我們使用線程池的方式就是new一個ThreadPoolExecutor對象來生成一個線程池。接下來,先看ThreadPoolExecutor類的構造函數:

//間接調用最後一個構造函數,採用默認的任務拒絕策略AbortPolicy和默認的線程工廠
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue);
    //間接調用最後一個構造函數,採用默認的任務拒絕策略AbortPolicy
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory);
    //間接調用最後一個構造函數,採用默認的默認的線程工廠
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler);
    //前面三個分別調用了最後一個,主要的構造函數
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler);

接下來,看下最後一個構造函數的具體實現:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        //參數合法性校驗
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        //參數合法性校驗
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        //初始化對應的屬性
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

下面解釋下一下構造器中各個參數的含義:

1.corePoolSize


線程池中的核心線程數。當提交一個任務時,線程池創建一個新線程執行任務,直到當前線程數等於corePoolSize;如果當前線程數爲corePoolSize,繼續提交的任務被保存到阻塞隊列中,等待被執行。

2.maximumPoolSize

線程池中允許的最大線程數。如果當前阻塞隊列滿了,且繼續提交任務,則創建新的線程執行任務,前提是當前線程數小於maximumPoolSize。

3.keepAliveTime


線程空閒時的存活時間。默認情況下,只有當線程池中的線程數大於corePoolSize時,keepAliveTime纔會起作用,如果一個線程空閒的時間達到keepAliveTime,則會終止,直到線程池中的線程數不超過corePoolSize。但是如果調用了allowCoreThreadTimeOut(boolean)方法,keepAliveTime參數也會起作用,直到線程池中的線程數爲0。

4.unit


keepAliveTime參數的時間單位。

5.workQueue


任務緩存隊列,用來存放等待執行的任務。如果當前線程數爲corePoolSize,繼續提交的任務就會被保存到任務緩存隊列中,等待被執行。


一般來說,這裏的BlockingQueue有以下三種選擇:

  • SynchronousQueue:一個不存儲元素的阻塞隊列,每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態。因此,如果線程池中始終沒有空閒線程(任務提交的平均速度快於被處理的速度),可能出現無限制的線程增長。

  • LinkedBlockingQueue:基於鏈表結構的阻塞隊列,如果不設置初始化容量,其容量爲Integer.MAX_VALUE,即爲無界隊列。因此,如果線程池中線程數達到了corePoolSize,且始終沒有空閒線程(任務提交的平均速度快於被處理的速度),任務緩存隊列可能出現無限制的增長。

  • ArrayBlockingQueue:基於數組結構的有界阻塞隊列,按FIFO排序任務。

6.threadFactory


線程工廠,創建新線程時使用的線程工廠。

7.handler


任務拒絕策略,當阻塞隊列滿了,且線程池中的線程數達到maximumPoolSize,如果繼續提交任務,就會採取任務拒絕策略處理該任務,線程池提供了4種任務拒絕策略:

  • AbortPolicy:丟棄任務並拋出RejectedExecutionException異常,默認策略;

  • CallerRunsPolicy:由調用execute方法的線程執行該任務;

  • DiscardPolicy:丟棄任務,但是不拋出異常;

  • DiscardOldestPolicy:丟棄阻塞隊列最前面的任務,然後重新嘗試執行任務(重複此過程)。

    當然也可以根據應用場景實現RejectedExecutionHandler接口,自定義飽和策略,如記錄日誌或持久化存儲不能處理的任務。


四.線程池的實現原理

1.提交任務

線程池框架提供了兩種方式提交任務,submit()和execute(),通過submit()方法提交的任務可以返回任務執行的結果,通過execute()方法提交的任務不能獲取任務執行的結果。

submit()方法的實現有以下三種:

public Future<?> submit(Runnable task);
    public <T> Future<T> submit(Runnable task, T result);
    public <T> Future<T> submit(Callable<T> task);

下面以第一個方法爲例簡單看一下submit()方法的實現:

public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

submit()方法是在ThreadPoolExecutor的父類AbstractExecutorService類實現的,最終還是調用的ThreadPoolExecutor類的execute()方法,下面着重看一下execute()方法的實現。

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        //獲取線程池控制狀態
        int c = ctl.get();
        // (1)
        //worker數量小於corePoolSize
        if (workerCountOf(c) < corePoolSize) {
            //創建worker,addWorker方法boolean參數用來判斷是否創建核心線程
            if (addWorker(command, true))
                //成功則返回
                return;
            //失敗則再次獲取線程池控制狀態
            c = ctl.get();
        }
        //(2)
       //線程池處於RUNNING狀態,將任務加入workQueue任務緩存隊列
        if (isRunning(c) && workQueue.offer(command)) {
            // 再次檢查,獲取線程池控制狀態,防止在任務入隊的過程中線程池關閉了或者線程池中沒有線程了
            int recheck = ctl.get();
            //線程池不處於RUNNING狀態,且將任務從workQueue移除成功
            if (! isRunning(recheck) && remove(command))
                //採取任務拒絕策略
                reject(command);
            //worker數量等於0
            else if (workerCountOf(recheck) == 0)
                //創建worker
                addWorker(null, false);
        }
        //(3)
        else if (!addWorker(command, false))  //創建worker
            reject(command);  //如果創建worker失敗,採取任務拒絕策略
    }

execute()方法的執行流程可以總結如下:

  • 若線程池工作線程數量小於corePoolSize,則創建新線程來執行任務

  • 若工作線程數量大於或等於corePoolSize,則將任務加入BlockingQueue

  • 若無法將任務加入BlockingQueue(BlockingQueue已滿),且工作線程數量小於maximumPoolSize,則創建新的線程來執行任務

  • 若工作線程數量達到maximumPoolSize,則創建線程失敗,採取任務拒絕策略

可以結合下面的兩張圖來理解線程池提交任務的執行流程。

ThreadPoolExecutor的execute()方法的執行示意圖


2.創建線程

從execute()方法的實現可以看出,addWorker()方法主要負責創建新的線程並執行任務,代碼實現如下:   

//addWorker有兩個參數:Runnable類型的firstTask,用於指定新增的線程執行的第一個任務;boolean類型的core,表示是否創建核心線程
//該方法的返回值代表是否成功新增一個線程
 private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // (1)
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                //線程數超標,不能再創建線程,直接返回
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //CAS操作遞增workCount
                //如果成功,那麼創建線程前的所有條件校驗都滿足了,準備創建線程執行任務,退出retry循環
                //如果失敗,說明有其他線程也在嘗試往線程池中創建線程(往線程池提交任務可以是併發的),則繼續往下執行
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                //重新獲取線程池控制狀態
                c = ctl.get();
                // 如果線程池的狀態發生了變更,如有其他線程關閉了這個線程池,那麼需要回到外層的for循環
                if (runStateOf(c) != rs)
                    continue retry;
                //如果只是CAS操作失敗的話,進入內層的for循環就可以了
            }
        }

        //到這裏,創建線程前的所有條件校驗都滿足了,可以開始創建線程來執行任務
        //worker是否已經啓動
        boolean workerStarted = false;
        //是否已將這個worker添加到workers這個HashSet中
        boolean workerAdded = false;
        Worker w = null;
        try {
            //創建一個worker,從這裏可以看出對線程的包裝
            w = new Worker(firstTask);
            //取出worker中的線程對象,Worker的構造方法會調用ThreadFactory來創建一個新的線程
            final Thread t = w.thread;
            if (t != null) {
                //獲取全局鎖, 併發的訪問線程池workers對象必須加鎖,持有鎖的期間線程池也不會被關閉
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    //重新獲取線程池的運行狀態
                    int rs = runStateOf(ctl.get());

                    //小於SHUTTDOWN即RUNNING
                    //等於SHUTDOWN並且firstTask爲null,不接受新的任務,但是會繼續執行等待隊列中的任務
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        //worker裏面的thread不能是已啓動的
                        if (t.isAlive())
                            throw new IllegalThreadStateException();
                       //將新創建的線程加入到線程池中
                        workers.add(w);
                        int s = workers.size();
                        // 更新largestPoolSize
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                //線程添加線程池成功,則啓動新創建的線程
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            //若線程啓動失敗,做一些清理工作,例如從workers中移除新添加的worker並遞減wokerCount
            if (! workerStarted)
                addWorkerFailed(w);
        }
        //返回線程是否啓動成功
        return workerStarted;
    }

因爲代碼(1)處的邏輯不利於理解,我們通過(1)的等價實現來理解:   

if (rs>=SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
//等價實現
rs>=SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty())

其含義爲,滿足下列條件之一則直接返回false,線程創建失敗:

  • rs > SHUTDOWN,也就是STOP,TIDYING或TERMINATED,此時不再接受新的任務,且中斷正在執行的任務

  • rs = SHUTDOWN且firstTask != null,此時不再接受任務,但是仍會處理任務緩存隊列中的任務

  • rs = SHUTDOWN,隊列爲空

多說一句,若線程池處於 SHUTDOWN, firstTask 爲 null,且 workQueue 非空,那麼還得創建線程繼續處理任務緩存隊列中的任務。

總結一下,addWorker()方法完成了如下幾件任務:


1.原子性的增加workerCount


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


3.啓動worker對應的線程


4.若線程啓動失敗,回滾worker的創建動作,即從workers中移除新添加的worker,並原子性的減少workerCount

3.工作線程的實現

從addWorker()方法的實現可以看出,工作線程的創建和啓動都跟ThreadPoolExecutor中的內部類Worker有關。下面我們分析Worker類來看一下工作線程的實現。

Worker類繼承自AQS類,具有鎖的功能;實現了Runable接口,可以將自身作爲一個任務在線程中執行。

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable

Worker的主要字段就下面三個,代碼也比較簡單。

//用來封裝worker的線程,線程池中真正運行的線程,通過線程工廠創建而來
        final Thread thread;
        //worker所對應的第一個任務,可能爲空
        Runnable firstTask;
        //記錄當前線程完成的任務數
        volatile long completedTasks;

Worker的構造函數如下。

Worker(Runnable firstTask) {
            //設置AQS的state爲-1,在執行runWorker()方法之前阻止線程中斷
            setState(-1);
            //初始化第一個任務
            this.firstTask = firstTask;
            //利用指定的線程工廠創建一個線程,注意,參數是Worker實例本身this
            //也就是當執行start方法啓動線程thread時,真正執行的是Worker類的run方法
            this.thread = getThreadFactory().newThread(this);
        }

Worker類繼承了AQS類,重寫了其相應的方法,實現了一個自定義的同步器,實現了不可重入鎖。

//是否持有獨佔鎖
        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);
            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(); }

Worker類還提供了一箇中斷線程thread的方法。

void interruptIfStarted() {
            Thread t;
            //AQS狀態大於等於0,worker對應的線程不爲null,且該線程沒有被中斷
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }

再來看一下Worker類的run()方法的實現,會發現run()方法最終調用了ThreadPoolExecutor類的runWorker()方法。

public void run() {
            runWorker(this);
        }

4.線程複用機制

通過上文可以知道,worker中的線程start 後,執行的是worker的run()方法,而run()方法最終會調用ThreadPoolExecutor類的runWorker()方法,runWorker()方法實現了線程池中的線程複用機制。下面我們來看一下runWorker()方法的實現。

final void runWorker(Worker w) {
        //獲取當前線程
        Thread wt = Thread.currentThread();
        //獲取w的firstTask
        Runnable task = w.firstTask;
        //設置w的firstTask爲null
        w.firstTask = null;
        // 釋放鎖,設置AQS的state爲0,允許中斷
        w.unlock();
        //用於標識線程是否異常終止,finally中processWorkerExit()方法會有不同邏輯
        boolean completedAbruptly = true;
        try {
            //循環調用getTask()獲取任務,不斷從任務緩存隊列獲取任務並執行
            while (task != null || (task = getTask()) != null) {
                //進入循環內部,代表已經獲取到可執行的任務,則對worker對象加鎖,保證線程在執行任務過程中不會被中斷
                w.lock();
                if ((runStateAtLeast(ctl.get(), STOP) ||  //若線程池狀態大於等於STOP,那麼意味着該線程要中斷
                     (Thread.interrupted() &&      //線程被中斷
                      runStateAtLeast(ctl.get(), STOP))) &&  //且是因爲線程池內部狀態變化而被中斷
                    !wt.isInterrupted())           //確保該線程未被中斷
                    //發出中斷請求
                    wt.interrupt();
                try {
                    //開始執行任務前的Hook方法
                    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 {
                        //執行任務後的Hook方法
                        afterExecute(task, thrown);
                    }
                } finally {
                    //置空task,準備通過getTask()獲取下一個任務
                    task = null;
                    //completedTasks遞增
                    w.completedTasks++;
                    //釋放掉worker持有的獨佔鎖
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            //到這裏,線程執行結束,需要執行結束線程的一些清理工作
            //線程執行結束可能有兩種情況:
            //1.getTask()返回null,也就是說,這個worker的使命結束了,線程執行結束
            //2.任務執行過程中發生了異常
            //第一種情況,getTask()返回null,那麼getTask()中會將workerCount遞減
            //第二種情況,workerCount沒有進行處理,這個遞減操作會在processWorkerExit()中處理
            processWorkerExit(w, completedAbruptly);
        }
    }

runWorker()方法是線程池的核心,實現了線程池中的線程複用機制,來看一下


runWorker()方法都做了哪些工作:


1.運行第一個任務firstTask之後,循環調用getTask()方法獲取任務,不斷從任務緩存隊列獲取任務並執行;


2.獲取到任務之後就對worker對象加鎖,保證線程在執行任務的過程中不會被中斷,任務執行完會釋放鎖;


3.在執行任務的前後,可以根據業務場景重寫beforeExecute()和afterExecute()等Hook方法;


4.執行通過getTask()方法獲取到的任務


5.線程執行結束後,調用processWorkerExit()方法執行結束線程的一些清理工作

從runWorker()方法的實現可以看出,runWorker()方法中主要調用了getTask()方法和processWorkerExit()方法,下面分別看一下這兩個方法的實現。

getTask()的實現

getTask()方法用來不斷地從任務緩存隊列獲取任務並交給線程執行,下面分析一下其實現。

private Runnable getTask() {
        //標識當前線程是否超時未能獲取到task對象
        boolean timedOut = false;

        for (;;) {
            //獲取線程池的控制狀態
            int c = ctl.get();
            //獲取線程池的運行狀態
            int rs = runStateOf(c);

            //如果線程池狀態大於等於STOP,或者處於SHUTDOWN狀態,並且阻塞隊列爲空,線程池工作線程數量遞減,方法返回null,回收線程
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
            
            //獲取worker數量
            int wc = workerCountOf(c);

            //標識當前線程在空閒時,是否應該超時回收
            // 如果allowCoreThreadTimeOut爲ture,或當前線程數大於核心池大小,則需要超時回收
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            
            //如果worker數量大於maximumPoolSize(有可能調用了 setMaximumPoolSize(),導致worker數量大於maximumPoolSize)
            if ((wc > maximumPoolSize || (timed && timedOut))  //或者獲取任務超時
                && (wc > 1 || workQueue.isEmpty())) {  //workerCount大於1或者阻塞隊列爲空(在阻塞隊列不爲空時,需要保證至少有一個工作線程)
                if (compareAndDecrementWorkerCount(c))
                    //線程池工作線程數量遞減,方法返回null,回收線程
                    return null;
                //線程池工作線程數量遞減失敗,跳過剩餘部分,繼續循環
                continue;
            }

            try {
                //如果允許超時回收,則調用阻塞隊列的poll(),只在keepAliveTime時間內等待獲取任務,一旦超過則返回null
                //否則調用take(),如果隊列爲空,線程進入阻塞狀態,無限時等待任務,直到隊列中有可取任務或者響應中斷信號退出
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                //若task不爲null,則返回成功獲取的task對象
                if (r != null)
                    return r;
                // 若返回task爲null,表示線程空閒時間超時,則設置timeOut爲true
                timedOut = true;
            } catch (InterruptedException retry) {
                //如果此worker發生了中斷,採取的方案是重試,沒有超時
                //在哪些情況下會發生中斷?調用setMaximumPoolSize(),shutDown(),shutDownNow()
                timedOut = false;
            }
        }
    }

接下來總結一下getTask()方法會在哪些情況下返回:


1.線程池處於RUNNING狀態,阻塞隊列不爲空,返回成功獲取的task對象


2.線程池處於SHUTDOWN狀態,阻塞隊列不爲空,返回成功獲取的task對象


3.線程池狀態大於等於STOP,返回null,回收線程


4.線程池處於SHUTDOWN狀態,並且阻塞隊列爲空,返回null,回收線程


5.worker數量大於maximumPoolSize,返回null,回收線程


6.線程空閒時間超時,返回null,回收線程

processWorkerExit()的實現

processWorkerExit()方法負責執行結束線程的一些清理工作,下面分析一下其實現。

private void processWorkerExit(Worker w, boolean completedAbruptly) {
        //如果用戶任務執行過程中發生了異常,則需要遞減workerCount
        if (completedAbruptly)
            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) {  //如果用戶任務執行過程中發生了異常,則直接調用addWorker()方法創建線程
                //是否允許核心線程超時
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                //允許核心超時並且workQueue阻塞隊列不爲空,那線程池中至少有一個工作線程
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                //如果工作線程數量workerCount大於等於核心池大小corePoolSize,
                //或者允許核心超時並且workQueue阻塞隊列不爲空時,線程池中至少有一個工作線程,直接返回
                if (workerCountOf(c) >= min)
                    return;
                //若不滿足上述條件,則調用addWorker()方法創建線程
            }
            //創建新的線程取代當前線程
            addWorker(null, false);
        }
    }

processWorkerExit()方法中主要調用了tryTerminate()方法,下面看一下tryTerminate()方法的實現。

final void tryTerminate() {
        for (;;) {
            //獲取線程池控制狀態
            int c = ctl.get();
            if (isRunning(c) ||    //線程池的運行狀態爲RUNNING
                runStateAtLeast(c, TIDYING) ||    //線程池的運行狀態大於等於TIDYING
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))  //線程池的運行狀態爲SHUTDOWN且阻塞隊列不爲空
                //不能終止,直接返回
                return;

            //只有當線程池的運行狀態爲STOP,或線程池運行狀態爲SHUTDOWN且阻塞隊列爲空時,可以執行到這裏
            //如果線程池工作線程的數量不爲0
            if (workerCountOf(c) != 0) {
                //僅僅中斷一個空閒的worker
                interruptIdleWorkers(ONLY_ONE);
                return;
            }

            //只有當線程池工作線程的數量爲0時可以執行到這裏
            final ReentrantLock mainLock = this.mainLock;
            //獲取全局鎖
            mainLock.lock();
            try {
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {  //CAS操作設置線程池運行狀態爲TIDYING,工作線程數量爲0
                    try {
                        //執行terminated()鉤子方法
                        terminated();
                    } finally {
                        //設置線程池運行狀態爲TERMINATED,工作線程數量爲0
                        ctl.set(ctlOf(TERMINATED, 0));
                        //喚醒在termination條件上等待的所有線程
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                //釋放鎖
                mainLock.unlock();
            }
            //若CAS操作失敗則重試
        }
    }

tryTerminate()方法的作用是嘗試終止線程池,它會在所有可能終止線程池的地方被調用,滿足終止線程池的條件有兩個:首先,線程池狀態爲STOP,或者爲SHUTDOWN且任務緩存隊列爲空;其次,工作線程數量爲0。

滿足了上述兩個條件之後,tryTerminate()方法獲取全局鎖,設置線程池運行狀態爲TIDYING,之後執行terminated()鉤子方法,最後設置線程池狀態爲TERMINATED。

至此,線程池運行狀態變爲TERMINATED,工作線程數量爲0,workers已清空,且workQueue也已清空,所有線程都執行結束,線程池的生命週期到此結束。

5.關閉線程池

關閉線程池有兩個方法,shutdown()和shutdownNow(),下面分別看一下這兩個方法的實現。

shutdown()的實現

shutdown()方法將線程池運行狀態設置爲SHUTDOWN,此時線程池不會接受新的任務,但會處理阻塞隊列中的任務。

public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        //獲取全局鎖
        mainLock.lock();
        try {
            //檢查shutdown權限
            checkShutdownAccess();
            //設置線程池運行狀態爲SHUTDOWN
            advanceRunState(SHUTDOWN);
            //中斷所有空閒worker
            interruptIdleWorkers();
            //用onShutdown()鉤子方法
            onShutdown();
        } finally {
            //釋放鎖
            mainLock.unlock();
        }
        //嘗試終止線程池
        tryTerminate();
    }

shutdown()方法首先會檢查是否具有shutdown的權限,然後設置線程池的運行狀態爲SHUTDOWN,之後中斷所有空閒的worker,再調用onShutdown()鉤子方法,最後嘗試終止線程池。

shutdown()方法調用了interruptIdleWorkers()方法中斷所有空閒的worker,其實現如下。

private void interruptIdleWorkers() {
        interruptIdleWorkers(false);
    }

    //onlyOne標識是否只中斷一個線程
    private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        //獲取全局鎖
        mainLock.lock();
        try {
            //遍歷workers集合
            for (Worker w : 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();
        }
    }

shutdownNow()的實現

shutdownNow()方法將線程池運行狀態設置爲STOP,此時線程池不會接受新任務,也不會處理阻塞隊列中的任務,並且中斷正在運行的任務。

public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        //獲取全局鎖
        mainLock.lock();
        try {
            //檢查shutdown權限
            checkShutdownAccess();
            //設置線程池運行狀態爲STOP
            advanceRunState(STOP);
            //中斷所有worker
            interruptWorkers();
            //將任務緩存隊列中等待執行的任務取出並放到list中
            tasks = drainQueue();
        } finally {
            //釋放鎖
            mainLock.unlock();
        }
        //嘗試終止線程池
        tryTerminate();
        //返回任務緩存隊列中等待執行的任務列表
        return tasks;
    }

shutdownNow()方法與shutdown()方法相似,不同之處在於,前者設置線程池的運行狀態爲STOP,之後中斷所有的worker(並非只是空閒的worker),嘗試終止線程池之後,返回任務緩存隊列中等待執行的任務列表。

shutdownNow()方法調用了interruptWorkers()方法中斷所有的worker(並非只是空閒的worker),其實現如下。

private void interruptWorkers() {
        final ReentrantLock mainLock = this.mainLock;
        //獲取全局鎖
        mainLock.lock();
        try {
            //遍歷workers集合
            for (Worker w : workers)
                //調用Worker類的interruptIfStarted()方法中斷線程
                w.interruptIfStarted();
        } finally {
            //釋放鎖
            mainLock.unlock();
        }
    }


五.總結

至此,我們已經閱讀了線程池框架的核心類ThreadPoolExecutor類的大部分源碼,由衷地讚歎這個類很多地方設計的巧妙之處:

  • 將線程池的運行狀態和工作線程數量打包在一起,並使用了大量的位運算

  • 使用CAS操作更新線程控制狀態ctl,確保對ctl的更新是原子操作

  • 內部類Worker類繼承了AQS,實現了一個自定義的同步器,實現了不可重入鎖

  • 使用while循環自旋地從任務緩存隊列中獲取任務並執行,實現了線程複用機制

  • 調用interrupt()方法中斷線程,但注意該方法並不能直接中斷線程的運行,只是發出了中斷信號,配合BlockingQueue的take(),poll()方法的使用,打斷線程的阻塞狀態

其實,線程池的本質就是生產者消費者模式,線程池的調用者不斷向線程池提交任務,線程池裏面的工作線程不斷獲取這些任務並執行(從任務緩存隊列獲取任務或者直接執行任務)。

讀完本文,相信大家對線程池的實現原理有了深刻的認識,比如向線程池提交一個任務之後線程池的執行流程,一個任務從被提交到被執行會經歷哪些過程,一個工作線程從被創建到正常執行到執行結束的執行過程,等等。

END

關注我
公衆號(zhisheng)裏回覆 面經、ES、Flink、 Spring、Java、Kafka、監控 等關鍵字可以查看更多關鍵字對應的文章。

你點的每個贊,我都認真當成了喜歡

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