線程池ThreadPoolExecutor分析: 線程池是什么時候創建線程的,隊列中的任務是什么時候取出來的?


 

帶着幾個問題進入源碼分析:

1. 線程池是什么時候創建線程的?

2. 任務runnable task是先放到core到maxThread之間的線程,還是先放到隊列?

3. 隊列中的任務是什么時候取出來的?

4. 什么時候會觸發reject策略?

5. core到maxThread之間的線程什么時候會die?

6. task拋出異常,線程池中這個work thread還能運行其他任務嗎?

 

先寫一段基礎代碼,進入分析

public static void main(String[] args) {

        ExecutorService executorService = new ThreadPoolExecutor(2, 5, 0, TimeUnit.DAYS,
                new ArrayBlockingQueue<>(1), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
//                thread.setDaemon(true);
                return thread;
            }
        });
        // 對象創建后,線程實際還沒開始創建

        // 執行execute時,檢查當前池中線程數大小是否小於core number, 如果是,則創建新線程
        executorService.execute(() -> {
            System.out.println("任務1@" + Thread.currentThread().getName());
            sleepTime();
            System.out.println(1);
        });

        //檢查當前池中線程數大小是否小於core number, 如果是,則創建新線程
        executorService.execute(() -> {
            System.out.println("任務2@" + Thread.currentThread().getName());
            sleepTime();
            System.out.println(2);
        });

        // 檢查當前池中線程數大小是否小於core number, 如果不是,則償試放入隊列
        // 這個任務是加到隊列去的, 注意隊列大小只有1,
        // TODO 隊列中的任務是什么時候取出來的?   任務1或者2結束后所占用的線程 會運行隊列中的任務,這個任務是在最后才運行,比4運行的還晚
        executorService.execute(() -> {
            System.out.println("任務3@" + Thread.currentThread().getName());
            sleepTime();
            System.out.println(3);
        });

        // 檢查當前池中線程數大小是否小於core number, 如果不是,則償試放入隊列,放入隊列也失敗,則增加新的worker線程
        // 這個任務是加到core以外的新線程去的
        executorService.execute(() -> {
            System.out.println("任務4@" + Thread.currentThread().getName());
            sleepTime();
            System.out.println(4);
        });
    }

  

注意第3行,創建一個核心池2, 最大池5, 隊列為1的線程池

至少在new ThreadPoolExecutor()時,Thread對象並沒有初始化. 這里僅僅指定了幾個初始參數

 

執行第一個execute時,進入調試jdk源碼

 代碼塊1

第一個if, 判斷如果當前線程數小於corePoolSize, 則創建新的核心worker對象(Worker中指向Thread對象,保持引用,保證不會被GC回收)

     我們的示例代碼中,第1和第2個任務都是這樣創建出線程的

第二個if,   判斷如果當前線程數大於corePoolSize, 並償試放入隊列 workQueue.offer(command) , 放入成功后等待線程池調度【見后面的getTask()】

     示例代碼中,第3個任務是這樣等待調度的,最后才執行

第三個if,  償試放入隊列 workQueue.offer(command) 失敗, 增加一個非core的線程

     示例代碼中,第4個任務是這樣開始的

 

 

然后再看addWorker()的過程

 

new Worker構造和線程啟動

線程啟動后,又做了哪些工作:

異常分析

沒拋異常時,此線程會一直在while(task !=null || (task = getTask())!=null)中跑

那么有異常時,再看一下processWorkerExit

 

可以 看出,有異常時 舊的worker會被刪除(GC回收),再創建新的Worker, 即有異常時 舊worker不可能再執行新的任務

 

 

總結:

 

Q. 線程池是什么時候創建線程的?

A.任務提交的時候

Q.任務runnable task是先放到core到maxThread之間的線程,還是先放到隊列?

A.先放隊列!!!

Q. 隊列中的任務是什么時候取出來的?

A. worker中 runWorker() 一個任務完成后,會取下一個任務

Q. 什么時候會觸發reject策略?

A.隊列滿並且maxthread也滿了, 還有新任務,默認策略是reject

Q. core到maxThread之間的線程什么時候會die?

A.  沒有任務時,或者拋異常時。   

   core線程也會die的,core到maxThread之間的線程有可能會晉升到core線程區間,

   core max只是個計數,線程並不是創建后就固定在一個區間了

Q. task拋出異常,線程池中這個work thread還能運行其他任務嗎?

A. 不能。 但是會創建新的線程, 新線程可以運行其他task。

對於 schedulerThreadPoolExecutor?   雖然有新線程,但是舊的循環任務不會再繼續執行了, 開發實踐推薦任務中捕獲所有Exception

 


免責聲明!

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



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