天天看點

ThreadPoolExecutor源碼分析(二):任務送出主邏輯execute()

        作為一個執行服務ExecutorService的執行個體,ThreadPoolExecutor的首要任務當然是送出任務進行處理,那麼,在任務送出時,ThreadPoolExecutor的處理流程是怎樣的?它是一味的開啟線程進行處理,還是有一套完整的邏輯來處理呢?本文,我們将繼續分析ThreadPoolExecutor,看下它在任務送出時的主邏輯。

        任務的送出是通過execute()方法完成的,代碼如下:

        execute()方法的運作邏輯比較簡單,它會首先檢查待處理任務command是否為空,為空的話直接抛出空指針NullPointerException異常,并擷取ctl的值c,ctl我們在上一篇文章中已講到過,它是ThreadPoolExecutor目前線程池的一個綜合控制狀态,然後再根據目前ThreadPoolExecutor具體情況做如下處理:

        1、如果目前線程池有效線程數目小于corePoolSize,那麼嘗試添加新的worker線程處理任務command:

              從c中擷取有效線程數目調用的是workerCountOf()方法,添加新的worker線程處理任務command調用的是addWorker()方法,且第二個參數為true表示線程數的判斷利用corePoolSize作為邊界限制條件,方法傳回值是标志添加worker是否成功的标志位,ture表示成功,false表示失敗,如果為true,則直接傳回,否則重新擷取ctl的值c;

        2、根據c判斷目前線程池的狀态是否為RUNNING狀态,即既可以接受新任務,又會處理隊列任務的狀态,注意此時c是重新擷取的最新的資料,并且通過offer()方法,嘗試将commond添加到隊列workQueue中,BlockingQueue的offer()方法表示如果可能的話,将參數對象加到BlockingQueue裡,即如果BlockingQueue可以容納,則傳回true,否則傳回false:

              此時,如果目前線程池處于RUNNING狀态,且workQueue能夠容納command,并添加成功的話,再次擷取ctl的值recheck,做第二次檢查,如果目前線程池的狀态不是RUNNING,并且從隊列workQueue移除command成功的話,否則如果目前工作線程woker數目為0,嘗試添加新的worker線程,但是不攜帶任務;

        3、如果嘗試添加新的worker線程處理任務command失敗,調用reject()方法拒絕任務command,這時線程數的判斷利用maximumPoolSize作為邊界限制條件。

        從上面的邏輯我們大緻可以分析出如下一個主要思路:

        1、如果ThreadPoolExecutor中目前有效線程數低于corePoolSize,那麼我們總是優先嘗試啟動新的worker線程處理任務command;

        2、如果ThreadPoolExecutor中目前有效線程數高于或等于corePoolSize,亦或者低于的情況下啟動新的worker線程失敗,我們會根據目前線程池狀态是否為RUNNING,将任務command添加到任務隊列workQueue中;

        3、最後實在不行,在maximumPoolSize允許的情況下,強行開啟一個線程處理任務。

        這裡,還有一點需要說明,在第2步中,既然已經将任務成功加入workQueue隊列,為什麼還需要再次做線程池狀态檢查呢?因為在我們剛剛把任務成功添加到任務隊列workQueue中的那一刻,或者前一刻,任務還是RUNNING狀态,但是保不齊其他調用者或者線程此時會修改線程池狀态,那麼此時我們就需要将任務再進行必要的移除,這是考慮複雜情況的一種安全機制的保障!