理解ThreadPoolExecutor源碼(一)線程池的corePoolSize、maximumPoolSize和poolSize

我們知道,受限於硬件、內存和性能,我們不可能無限制的創建任意數量的線程,因爲每一臺機器允許的最大線程是一個有界值。也就是說ThreadPoolExecutor管理的線程數量是有界的。線程池就是用這些有限個數的線程,去執行提交的任務。然而對於多用戶、高併發的應用來說,提交的任務數量非常巨大,一定會比允許的最大線程數多很多。爲了解決這個問題,必須要引入排隊機制,或者是在內存中,或者是在硬盤等容量很大的存儲介質中。J.U.C提供的ThreadPoolExecutor只支持任務在內存中排隊,通過BlockingQueue暫存還沒有來得及執行的任務。

 

任務的管理是一件比較容易的事,複雜的是線程的管理,這會涉及線程數量、等待/喚醒、同步/鎖、線程創建和死亡等問題。ThreadPoolExecutor與線程相關的幾個成員變量是:keepAliveTime、allowCoreThreadTimeOut、poolSize、corePoolSize、maximumPoolSize,它們共同負責線程的創建和銷燬。

corePoolSize

線程池的基本大小,即在沒有任務需要執行的時候線程池的大小,並且只有在工作隊列滿了的情況下才會創建超出這個數量的線程。這裏需要注意的是:在剛剛創建ThreadPoolExecutor的時候,線程並不會立即啓動,而是要等到有任務提交時纔會啓動,除非調用了prestartCoreThread/prestartAllCoreThreads事先啓動核心線程。再考慮到keepAliveTime和allowCoreThreadTimeOut超時參數的影響,所以沒有任務需要執行的時候,線程池的大小不一定是corePoolSize

maximumPoolSize:

線程池中允許的最大線程數,線程池中的當前線程數目不會超過該值。如果隊列中任務已滿,並且當前線程個數小於maximumPoolSize,那麼會創建新的線程來執行任務。這裏值得一提的是largestPoolSize,該變量記錄了線程池在整個生命週期中曾經出現的最大線程個數。爲什麼說是曾經呢?因爲線程池創建之後,可以調用setMaximumPoolSize()改變運行的最大線程的數目。

poolSize:

線程池中當前線程的數量,當該值爲0的時候,意味着沒有任何線程,線程池會終止;同一時刻,poolSize不會超過maximumPoolSize。


現在我們通過ThreadPoolExecutor.execute()方法,看一下這3個屬性的關係,以及線程池如何處理新提交的任務。以下源碼基於JDK1.6.0_37版本。

[java] view plain copy
  1. public void execute(Runnable command) {  
  2.     if (command == null)  
  3.         throw new NullPointerException();  
  4.     if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {  
  5.         if (runState == RUNNING && workQueue.offer(command)) {  
  6.             if (runState != RUNNING || poolSize == 0)  
  7.                 ensureQueuedTaskHandled(command);  
  8.         }  
  9.         else if (!addIfUnderMaximumPoolSize(command))  
  10.             reject(command); // is shutdown or saturated  
  11.     }  
  12. }  
  13.   
  14.   
  15. private boolean addIfUnderCorePoolSize(Runnable firstTask) {  
  16.     Thread t = null;  
  17.     final ReentrantLock mainLock = this.mainLock;  
  18.     mainLock.lock();  
  19.     try {  
  20.         if (poolSize < corePoolSize && runState == RUNNING)  
  21.             t = addThread(firstTask);  
  22.     } finally {  
  23.         mainLock.unlock();  
  24.     }  
  25.     if (t == null)  
  26.         return false;  
  27.     t.start();  
  28.     return true;  
  29. }  
  30.   
  31. private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {  
  32.     Thread t = null;  
  33.     final ReentrantLock mainLock = this.mainLock;  
  34.     mainLock.lock();  
  35.     try {  
  36.         if (poolSize < maximumPoolSize && runState == RUNNING)  
  37.             t = addThread(firstTask);  
  38.     } finally {  
  39.         mainLock.unlock();  
  40.     }  
  41.     if (t == null)  
  42.         return false;  
  43.     t.start();  
  44.     return true;  
  45. }  
新提交一個任務時的處理流程很明顯:

1、如果線程池的當前大小還沒有達到基本大小(poolSize < corePoolSize),那麼就新增加一個線程處理新提交的任務;

2、如果當前大小已經達到了基本大小,就將新提交的任務提交到阻塞隊列排隊,等候處理workQueue.offer(command);

3、如果隊列容量已達上限,並且當前大小poolSize沒有達到maximumPoolSize,那麼就新增線程來處理任務;

4、如果隊列已滿,並且當前線程數目也已經達到上限,那麼意味着線程池的處理能力已經達到了極限,此時需要拒絕新增加的任務。至於如何拒絕處理新增

      的任務,取決於線程池的飽和策略RejectedExecutionHandler。


接下來我們看下allowCoreThreadTimeOut和keepAliveTime屬性的含義。在壓力很大的情況下,線程池中的所有線程都在處理新提交的任務或者是在排隊的任務,這個時候線程池處在忙碌狀態。如果壓力很小,那麼可能很多線程池都處在空閒狀態,這個時候爲了節省系統資源,回收這些沒有用的空閒線程,就必須提供一些超時機制,這也是線程池大小調節策略的一部分。通過corePoolSize和maximumPoolSize,控制如何新增線程;通過allowCoreThreadTimeOut和keepAliveTime,控制如何銷燬線程。

allowCoreThreadTimeOut:

該屬性用來控制是否允許核心線程超時退出。If false,core threads stay alive even when idle.If true, core threads use keepAliveTime to time out waiting for work。如果線程池的大小已經達到了corePoolSize,不管有沒有任務需要執行,線程池都會保證這些核心線程處於存活狀態。可以知道:該屬性只是用來控制核心線程的。

keepAliveTime:

如果一個線程處在空閒狀態的時間超過了該屬性值,就會因爲超時而退出。舉個例子,如果線程池的核心大小corePoolSize=5,而當前大小poolSize =8,那麼超出核心大小的線程,會按照keepAliveTime的值判斷是否會超時退出。如果線程池的核心大小corePoolSize=5,而當前大小poolSize =5,那麼線程池中所有線程都是核心線程,這個時候線程是否會退出,取決於allowCoreThreadTimeOut。

[java] view plain copy
  1. Runnable getTask() {  
  2.         for (;;) {  
  3.             try {  
  4.                 int state = runState;  
  5.                 if (state > SHUTDOWN)  
  6.                     return null;  
  7.                 Runnable r;  
  8.                 if (state == SHUTDOWN)  // Help drain queue  
  9.                     r = workQueue.poll();  
  10.                 else if (poolSize > corePoolSize || allowCoreThreadTimeOut)  
  11.                     r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);  
  12.                 else  
  13.                     r = workQueue.take();  
  14.                 if (r != null)  
  15.                     return r;  
  16.                 if (workerCanExit()) {  
  17.                     if (runState >= SHUTDOWN) // Wake up others  
  18.                         interruptIdleWorkers();  
  19.                     return null;  
  20.                 }  
  21.                 // Else retry  
  22.             } catch (InterruptedException ie) {  
  23.                 // On interruption, re-check runState  
  24.             }  
  25.         }  
  26.     }  

(poolSize > corePoolSize || allowCoreThreadTimeOut)這個條件,就是用來判斷是否允許當前線程退出。workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);就是藉助阻塞隊列,讓空閒線程等待keepAliveTime時間之後,恢復執行。這樣空閒線程會由於超時而退出。

轉:https://blog.csdn.net/aitangyong/article/details/38822505

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