王小二實習日記 -------- 線程池從入門到放棄

王小二最近剛剛拿到了一家互聯網公司的java研發的實習offer,激動的他很快便前往公司去實習了。

趙鐵柱是王小二的上司,開工的第一天便給王小二安排了一個需求,要求他開發一個功能,每天在指定的時間點運行各種各樣的任務。王小二靈機一動,立馬就想到了線程池

 

      

谷歌了一下,王小二直接將網上的代碼copy到了公司的項目裏面,然後提交轉測。

結果沒想到在壓測環節便出現了異常情況,於是趙鐵柱便找到了王小二詢問原因,通過代碼審查,看到這樣一段代碼:


ExecutorService executorService=Executors.newCachedThreadPool();
executorService.execute(task);

於是趙鐵柱便開始對王小二進行了質問,爲何要採用這樣的寫法,由於每個運行的任務都對系統本身有較大的的負載,所以導致了線程創建過多,耗盡cpu導致異常。

王小二作爲新來的實習生,對線程池的內部的構造原理並不是很熟悉,纔會犯下這樣的錯誤,於是趙鐵柱便開始耐心地給他講解了線程池的原理。

 

              

常見的幾種線程池類型

1.newCachedThreadPool

newCachedThreadPool是一種可緩存的線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。這類線程池的底層源碼爲:


 public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

當使用了該類線程池的時候容易有幾種情況發生:

  1. 由於設置的maximumPoolSize爲Integer.MAX_VALUE,因此創建的線程數量幾乎是沒有上限的,存在一定的風險性。         

  2. keepAliveTime是指線程的生命週期,當一個線程創建了之後,這裏默認設置爲了60秒,如果60秒內該線程一直處於空閒狀態的話,那麼該線程就會被回收,減少資源的消耗。當然,回收之後如果又有新的任務要提交,那麼還是可以重新創建線程的。                                                                             

  3. SynchronousQueue是一種不存儲任何元素的堵塞隊列,每一次執行插入操作的時候都需要等待到另一個線程的移除操作。這也就容易導致一個問題,如果加入的線程沒有被消費,那麼就會一直堵塞其中,導致後續的任務無法繼續加入。

2.newFixedThreadPool

FixedThreadPool是一款比較好用而且較爲優秀的線程池,在線程池進行初始化操作的時候,可以設定初始化線程池的線程個數。下邊我們來看看newFixedThreadPool的源代碼部分:


public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

這裏的兩個corePoolSize和maximumPoolSize參數都設置爲相同的nThreads形參值,然後keepAliveTime值設置爲了0,說明一旦有多餘的空閒線程就會立馬進行回收操作,從而減少消耗。當然啦,這裏採用的隊列結構是LinkedBlockingQueue。

3.newSingleThreadExecutor

SingleThreadExecutor是一個單線程化的Executor,只創建唯一的工作者線程來執行任務,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。如果這個線程異常結束,會有另一個取代它,保證順序執行。單工作線程最大的特點是可保證順序地執行各個任務,並且在任意給定的時間不會有多個線程是活動的。

相應的源碼定義如下:


public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

 

4.newScheduleThreadPool

ScheduleThreadPool也是繼承自ThreadPoolExecutor,主要的應用場景是執行一些定時任務。在應用方面有點類似於Timer,但是卻與Timer有着本質上區別,ScheduleThreadPool更加的靈活更加的強大,Timer只是一個單線程在運行任務,如果運行期間出現了程序崩潰的情況,則會波及到後邊所要執行的任務,而ScheduleThreadPool可以指定多個線程數目來執行任務,各個線程執行的任務相互獨立,不會互相波及。

相應的源碼定義如下:

Executors內部的定義:


public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

ScheduledThreadPoolExecutor內部的定義:


public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

最主要常用的線程池就是這幾類了,但是在Executors的內部其實還隱藏了很多的細節內容。

 

在老大的簡單介紹之後,王小二大概地對線程池有了個初步的瞭解。

 

              

我們從整個Execute的框構來看,Executor接口底下有着非常多的繼承和實現。

             

而在ExecutorService裏面則包含了非常重要的一系列具體方法:

1,execute(Runnable command):履行Ruannable類型的任務,

2,submit(task):可用來提交Callable或Runnable任務,並返回代表此任務的Future對象

3,shutdown():在完成已提交的任務後封閉辦事,不再接管新任務,

4,shutdownNow():停止所有正在履行的任務並封閉辦事。

5,isTerminated():測試是否所有任務都履行完畢了。

6,isShutdown():測試是否該ExecutorService已被關閉。

線程池的重要屬性

在ThreadPoolExecutor裏面包含有多個參數,每個不同的參數都表示了不同的含義。我們來大致認識一下下列的這些屬性含義:


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; }

在這段代碼裏面,有個關鍵的變量叫做ctl ,該變量的主要作用是對線程池的整體運行狀態和線程池中的worker線程數量進行控制的字段,這裏我們可以理解爲通過二進制的轉換將線程池的運行狀態和worker數量統一用一個數值變量來表示,該變量的高3位表示爲當前線程池的狀態,低的29位表示爲當前worker線程的數量。

       

線程池的狀態有哪些

一共有5種狀態,代碼的描述爲:

RUNNING = ­1 << COUNT_BITS; //高3位爲111
SHUTDOWN = 0 << COUNT_BITS; //高3位爲000
STOP = 1 << COUNT_BITS; //高3位爲001
TIDYING = 2 << COUNT_BITS; //高3位爲010
TERMINATED = 3 << COUNT_BITS; //高3位爲011

1、RUNNING

(1) 狀態說明:線程池處在RUNNING狀態時,能夠接收新任務,以及對已添加的任務進行處理。

(02) 狀態切換:線程池的初始化狀態是RUNNING。換句話說,線程池被一旦被創建,就處於RUNNING狀態,並且線程池中的任務數爲0!

2、 SHUTDOWN

(1) 狀態說明:線程池處在SHUTDOWN狀態時,不接收新任務,但能處理已添加的任務。

(2) 狀態切換:調用線程池的shutdown()接口時,線程池由RUNNING -> SHUTDOWN。

3、STOP

(1) 狀態說明:線程池處在STOP狀態時,不接收新任務,不處理已添加的任務,並且會中斷正在處理的任務。

(2) 狀態切換:調用線程池的shutdownNow()接口時,線程池由(RUNNING or

SHUTDOWN ) -> STOP。

4、TIDYING

(1) 狀態說明:當所有的任務已終止,ctl記錄的”任務數量”爲0,線程池會變爲TIDYING狀態。當線程池變爲TIDYING狀態時,會執行鉤子函數terminated()。terminated()在ThreadPoolExecutor類中是空的,若用戶想在線程池變爲TIDYING時,進行相應的處理;

可以通過重載terminated()函數來實現。

(2) 狀態切換:當線程池在SHUTDOWN狀態下,阻塞隊列爲空並且線程池中執行的任務也爲空時,就會由 SHUTDOWN -> TIDYING。當線程池在STOP狀態下,線程池中執行的任務爲空時,就會由STOP -> TIDYING。

5、 TERMINATED

(1) 狀態說明:線程池徹底終止,就變成TERMINATED狀態。

(2) 狀態切換:線程池處在TIDYING狀態時,執行完terminated()之後,就會由 TIDYING -> TERMINATED。進入TERMINATED的條件如下:線程池不是RUNNING狀態;線程池狀態不是TIDYING狀態或TERMINATED狀態;如果線程池狀態是SHUTDOWN並且workerQueue爲空;workerCount爲0;設置TIDYING狀態成功。 

整體的線程池生命週期

            

此時的王小二開始聽得有點好奇了......

       

當worker線程忙不過來的時候該如何處理

我們從整體的運作流程來看看線程池的執行順序:

            

首先我們通過execute或者submit的形式將任務加入到線程池裏面,然後會有以下的幾種情況:

1.corePool裏面有空閒的核心線程可以執行該任務,則直接執行該任務

2.corePool已經滿了,需要創建多的線程來執行任務,於是這個時候就會創建新的非核心線程來執行該任務。

3.當非核心線程+核心線程的數目達到了上線的時候,這個時候相關的任務就需要加入到相應的任務隊列中了,然後等待線程去消費。

4.如果整個任務隊列都滿了的話,就會執行內置的RejectedExecutionHandler

 

RejectedExecutionHandler裏面包含了四種類型策略

1、AbortPolicy:直接拋出異常,默認策略;

2、CallerRunsPolicy:用調用者所在的線程來執行任務;

3、DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務,並執行當前任務;

4、DiscardPolicy:直接丟棄任務; 

 

     

 

源碼分析

在執行execute操作的時候,我們深入源碼來閱讀:

jdk1.8的源碼位置在ThreadPoolExecutor類的1332行

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
     //記錄當前線程池的 工作狀態(高3位)和worker線程數(後邊29位)
    int c = ctl.get();
    //首先判斷是否當前的worker線程數是否小於corePoolSize
    if (workerCountOf(c) < corePoolSize) {
        //創建worker線程,並且添加任務給worker線程
        //worker線程,包含有thread屬性和firsttask屬性,通常一個worker對應一個線程,但是不一定會有firstTask,因爲它有可能從隊列裏面取任務。第二個參數是指是否創建爲core線程,如果爲false則表示創建的是非核心線程。
        if (addWorker(command, true))
            return;
        // 由它可以獲取到當前有效的線程數和線程池的狀態
        c = ctl.get();
    }
  
    // 當前線程池是否是running狀態,如果是則加入任務到任務隊列中
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        //雙重判斷,如果任務剛放入隊列就關閉了線程池,那麼這個時候需要清除掉之前加入的任務,並且拋出飽和異常
        if (! isRunning(recheck) && remove(command))
            reject(command);
        //如果當前的核心worker線程數爲0,那麼就直接從隊列裏面去取任務並且創建非核心線程,有可能剛創建了線程,但是該線程執行完畢之後立馬就掛了
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    //創建非核心線程,並且綁定任務,如果失敗則拋出飽和異常
    else if (!addWorker(command, false))
        reject(command);
}

這段代碼裏面主要包含了線程池運作的核心邏輯思路,從創建核心線程,到擴展非核心線程,到添加進入任務隊列,再到飽和異常拋出,全都有涉及。

接下來我們來看worker的源碼:

worker本身是ThreadPoolExecutor裏面的一個私有類,該類繼承了aqs同步隊列器,同時實現了Runnable方法。

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. */
    final Thread thread;
    /** Initial task to run.  Possibly null. */
    Runnable firstTask;
    /** Per-thread task counter */
    // 當前線程完成的任務數量,日後可以通過查看該數目來統計該線程完成的任務數目
    volatile long completedTasks;


    /**
     * Creates with given first task and thread from ThreadFactory.
     * @param firstTask the first task (null if none)
     */
    Worker(Runnable firstTask) {
        //在reetrenlock裏面這個參數設置爲0,但是這裏設置爲了-1,主要是和線程池的中斷有關,後邊在runWorker方法中有個unlock操作和這裏有呼應。
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        //注意這裏的thread是指當前的worker類
        this.thread = getThreadFactory().newThread(this);
    }


    /** Delegates main run loop to outer runWorker  */
    //調用了runWorker函數,其實真正執行firstTask的run方法是在runWorker裏面
    public void run() {
        runWorker(this);
    }


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


    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) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }


    public void lock()        { acquire(1); }
    public boolean tryLock()  { return tryAcquire(1); }
    //這裏面的release方法包含有tryRelease的方法,所以上邊的tryRelease進行了重寫,特意將state設置爲0。
    public void unlock()      { release(1); }
    public boolean isLocked() { return isHeldExclusively(); }


    void interruptIfStarted() {
        Thread t;
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
}

Worker類

線程池中的每一個線程被封裝成一個Worker對象,ThreadPool維護的其實就是一組Worker對象,請參見上方源碼。

Worker類繼承了AQS,並實現了Runnable接口,注意其中的firstTask和thread屬性:firstTask用它來保存傳入的任務;thread是在調用構造方法時通過ThreadFactory來創建的線程,是用來處理任務的線程。

在 調 用 構 造 方 法 時 , 需 要 把 任 務 傳 入 , 這 裏 通 過getThreadFactory().newThread(this); 來新建一個線程, newThread方法傳入的參數是this,因爲Worker本身繼承了Runnable接口,也就是一個線程,所以一個Worker對象在啓動的時候會調用Worker類中的run方法。

Worker繼承了AQS,使用AQS來實現獨佔鎖的功能。爲什麼不使用ReentrantLock來實現呢?可以看到tryAcquire方法,它是不允許重入的,而ReentrantLock是允許重入的:

1. lock方法一旦獲取了獨佔鎖,表示當前線程正在執行任務中;

2. 如果正在執行任務,則不應該中斷線程;

3. 如果該線程現在不是獨佔鎖的狀態,也就是空閒的狀態,說明它沒有在處理任務, 這時可以對該線程進行中斷;

4. 線程池在執行shutdown方法或tryTerminate方法時會調用interruptIdleWorkers 方法來中斷空閒的線程,interruptIdleWorkers方法會使用tryLock方法來判斷線程池中的線程是否是空閒狀態;

5. 之所以設置爲不可重入,是因爲我們不希望任務在調用像setCorePoolSize

這樣的線程池控制方法時重新獲取鎖。如果使用ReentrantLock,它是可重入的,這樣如果在任務中調用瞭如setCorePoolSize這類線程池控制的方法,會中斷正在運行的線程。

 

addWorker方法

addWorker的主要工作是在線程池中創建一個新的線程並執行,firstTask參數 用

於指定新增的線程執行的第一個任務,core參數爲true表示在新增線程時會判斷當前活動線程數是否少於corePoolSize,false表示新增線程前需要判斷當前活動線程數是否少於maximumPoolSize,代碼如下:

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,則表示此時不再接收新任務;* 接着判斷以下3個條件,只要有1個不滿足,則返回false:
          * 1. rs == SHUTDOWN,這時表示關閉狀態,不再接受新提交的任務,但卻
          可以繼續處理阻塞隊列中已保存的任務
          * 2. firsTask爲空
          * 3. 阻塞隊列不爲空
          *
          * 首先考慮rs == SHUTDOWN的情況
          * 這種情況下不會接受新提交的任務,所以在firstTask不爲空的時候會返回
          false;
          * 然後,如果firstTask爲空,並且workQueue也爲空,則返回false,
          * 因爲隊列中已經沒有任務了,不需要再添加線程了
          */
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;


        for (;;) {
            int wc = workerCountOf(c);
            // 如果wc超過CAPACITY,也就是ctl的低29位的最大值(二進制是
             // 29個1),返回false;
              // 這裏的core是addWorker方法的第二個參數,如果爲true表示
             // 根據corePoolSize來比較,
              // 如果爲false則根據maximumPoolSize來比較。
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // 嘗試增加workerCount,如果成功就跳出循環
            if (compareAndIncrementWorkerCount(c))
                break retry;
            // 自增失敗就跳出循環
            c = ctl.get();  // Re-read ctl
            //對當前線程池的狀態進行校驗
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }


    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        //根據firstTask來創建Worker對象
        w = new Worker(firstTask);
        //一個worker對應一個線程
        final Thread t = w.thread;
        if (t != 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.
                // rs < SHUTDOWN表示是RUNNING狀態;
              // 如果rs是RUNNING狀態或者rs是SHUTDOWN狀態並且firstTask爲null,向線程池中添加線程。
                int rs = runStateOf(ctl.get());


                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                        //將woker加入到workers裏面,workers其實是一個hashSet來的。
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                //啓動線程,然後會調用worker的run方法,原因是worker在創建線程的時候,往線程工廠裏面注入了一個this對象。
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted
  }

getTask方法

getTask方法是用來從阻塞隊列中取任務,源代碼如下:

private Runnable getTask() {
        // timeOut變量的值表示上次從阻塞隊列中取任務時是否超時
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
            /*
            *如果線程池狀態rs >= SHUTDOWN,也就是非RUNNING狀態,再進行以下判斷:
            *1. rs >= STOP,線程池是否正在stop;
            *2. 阻塞隊列是否爲空。
            *如果以上條件滿足,則將workerCount減1並返回null。
            *因爲如果當前線程池狀態的值是SHUTDOWN或以上時,不允許再向阻塞隊列中添加任務。
           */
            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            // timed變量用於判斷是否需要進行超時控制。
            //  allowCoreThreadTimeOut默認是false,也就是核心線程不允許進行超時;
            
            // wc > corePoolSize,表示當前線程池中的線程數量大於核心線程數量;
            // 對於超過核心線程數量的這些線程,需要進行超時控制
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            
            /*
            * wc > maximumPoolSize的情況是因爲可能在此方法執行階段同時執行了setMaximumPoolSize方法;
            *timed && timedOut 如果爲true,表示當前操作需要進行超時控制,並且上次從阻塞隊列中獲取任務發生了超時
            *接下來判斷,如果有效線程數量大於1,或者阻塞隊列是空的,那麼嘗試將workerCount減1;
            *如果減1失敗,則返回重試。
            *如果wc == 1時,也就說明當前線程是線程池中唯一的一個線程了。
           */
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
            /*
              *根據timed來判斷,如果爲true,則通過阻塞隊列的poll方法進行超時控  制,如果在keepAliveTime時間內沒有獲取到任務,則返回null;
              *否則通過take方法,如果這時隊列爲空,則take方法會阻塞直到隊列不爲
              空。
              **/
                Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
                if (r != null)
                    return r;
             // 如果 r == null,說明已經超時,timedOut設置爲true
                timedOut = true;
            } catch (InterruptedException retry) {
            // 如果獲取任務時當前線程發生了中斷,則設置timedOut爲false並返回循環重試
                timedOut = false;
            }
        }
    }

這裏重要的地方是第二個if判斷,目的是控制線程池的有效線程數量。由上文中的分析可以知道,在執行execute方法時,如果當前線程池的線程數量超過了corePoolSize且小於maximumPoolSize,並且workQueue已滿時,則可以增加工作線程,但這時如果超時沒有獲取到任務,也就是timedOut爲true的情況,說明workQueue已經爲空了,也就說明了當前線程池中不需要那麼多線程來執行任務了,可以把多於corePoolSize數量的線程銷燬掉,保持線程數量在corePoolSize即可。

什麼時候會銷燬?

runWorker方法執行完之後,也就是Worker中的run方法執行完,由JVM自動回收。getTask 方法返回null 時, 在runWorker 方法中會跳出while 循環, 然後會執行processWorkerExit方法。

processWorkerExit方法

相關源代碼


private void processWorkerExit(Worker w, boolean completedAbruptly) {
        // 如果completedAbruptly值爲true,則說明線程執行時出現了異常,需要將workerCount減1;
        // 如果線程執行時沒有出現異常,說明在getTask()方法中已經已經對workerCount進行了減1操作,這裏就不必再減了。
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            //統計完成的任務數
            completedTaskCount += w.completedTasks;
            // 從workers中移除,也就表示着從線程池中移除了一個工作線程
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }
        // 根據線程池狀態進行判斷是否結束線程池
        tryTerminate();

        int c = ctl.get();
        /*
          *當線程池是RUNNING或SHUTDOWN狀態時,如果worker是異常結束,那麼會直接
          addWorker;
          *如果allowCoreThreadTimeOut=true,並且等待隊列有任務,至少保留一個
          worker;
          *如果allowCoreThreadTimeOut=false,workerCount不少於corePoolSize。
         */
        if (runStateLessThan(c, STOP)) {
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

至此,processWorkerExit執行完之後,工作線程被銷燬,以上就是整個工作線程的生命週期,從execute方法開始,Worker使用ThreadFactory創建新的工作線程,runWorker通過getTask獲取任務,然後執行任務,如果getTask返回null,進入processWorkerExit方法,整個線程結束,如圖所示:

 

 

 

 此時此刻,作爲實習生小白的王小二早已在“流”下了沒有技術的淚水,默默地回去改bug了......

 

 

 

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