咱們知道,受限於硬件、內存和性能,咱們不可能無限制的建立任意數量的線程,由於每一臺機器容許的最大線程是一個有界值。也就是說ThreadPoolExecutor管理的線程數量是有界的。線程池就是用這些有限個數的線程,去執行提交的任務。然而對於多用戶、高併發的應用來講,提交的任務數量很是巨大,必定會比容許的最大線程數多不少。爲了解決這個問題,必需要引入排隊機制,或者是在內存中,或者是在硬盤等容量很大的存儲介質中。J.U.C提供的ThreadPoolExecutor只支持任務在內存中排隊,經過BlockingQueue暫存尚未來得及執行的任務。多線程
任務的管理是一件比較容易的事,複雜的是線程的管理,這會涉及線程數量、等待/喚醒、同步/鎖、線程建立和死亡等問題。ThreadPoolExecutor與線程相關的幾個成員變量是:keepAliveTime、allowCoreThreadTimeOut、poolSize、corePoolSize、maximumPoolSize,它們共同負責線程的建立和銷燬。併發
corePoolSize:less
線程池的基本大小,即在沒有任務須要執行的時候線程池的大小,而且只有在工做隊列滿了的狀況下才會建立超出這個數量的線程。這裏須要注意的是:在剛剛建立ThreadPoolExecutor的時候,線程並不會當即啓動,而是要等到有任務提交時纔會啓動,除非調用了prestartCoreThread/prestartAllCoreThreads事先啓動核心線程。再考慮到keepAliveTime和allowCoreThreadTimeOut超時參數的影響,因此沒有任務須要執行的時候,線程池的大小不必定是corePoolSize。高併發
maximumPoolSize:性能
線程池中容許的最大線程數,線程池中的當前線程數目不會超過該值。若是隊列中任務已滿,而且當前線程個數小於maximumPoolSize,那麼會建立新的線程來執行任務。這裏值得一提的是largestPoolSize,該變量記錄了線程池在整個生命週期中曾經出現的最大線程個數。爲何說是曾經呢?由於線程池建立以後,能夠調用setMaximumPoolSize()改變運行的最大線程的數目。ui
poolSize:this
線程池中當前線程的數量,當該值爲0的時候,意味着沒有任何線程,線程池會終止;同一時刻,poolSize不會超過maximumPoolSize。spa
如今咱們經過ThreadPoolExecutor.execute()方法,看一下這3個屬性的關係,以及線程池如何處理新提交的任務。如下源碼基於JDK1.6.0_37版本。線程
private boolean addIfUnderCorePoolSize(Runnable firstTask) { Thread t = null; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (poolSize < corePoolSize && runState == RUNNING) t = addThread(firstTask); } finally { mainLock.unlock(); } if (t == null) return false; t.start(); return true; } private boolean addIfUnderMaximumPoolSize(Runnable firstTask) { Thread t = null; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (poolSize < maximumPoolSize && runState == RUNNING) t = addThread(firstTask); } finally { mainLock.unlock(); } if (t == null) return false; t.start(); return true; }
新提交一個任務時的處理流程很明顯:rest
一、若是當前線程池的線程數尚未達到基本大小(poolSize < corePoolSize),不管是否有空閒的線程新增一個線程處理新提交的任務;
二、若是當前線程池的線程數大於或等於基本大小(poolSize >= corePoolSize) 且任務隊列未滿時,就將新提交的任務提交到阻塞隊列排隊,等候處理workQueue.offer(command);
三、若是當前線程池的線程數大於或等於基本大小(poolSize >= corePoolSize) 且任務隊列滿時;
3.一、當前poolSize<maximumPoolSize,那麼就新增線程來處理任務;
3.二、當前poolSize=maximumPoolSize,那麼意味着線程池的處理能力已經達到了極限,此時須要拒絕新增長的任務。至於如何拒絕處理新增的任務,取決於線程池的飽和策略RejectedExecutionHandler。
Queuing
Any BlockingQueue may be used to transfer and hold submitted tasks. The use of this queue interacts with pool sizing:
- If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
- If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.
- If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.
1、corePoolSize:核心線程數 * 核心線程會一直存活,及時沒有任務須要執行 * 當線程數小於核心線程數時,即便有線程空閒,線程池也會優先建立新線程處理 * 設置allowCoreThreadTimeout=true(默認false)時,核心線程會超時關閉 2、queueCapacity:任務隊列容量(阻塞隊列) * 當核心線程數達到最大時,新任務會放在隊列中排隊等待執行 3、maxPoolSize:最大線程數 * 當線程數>=corePoolSize,且任務隊列已滿時。線程池會建立新線程來處理任務 * 當線程數=maxPoolSize,且任務隊列已滿時,線程池會拒絕處理任務而拋出異常 4、 keepAliveTime:線程空閒時間 * 當線程空閒時間達到keepAliveTime時,線程會退出,直到線程數量=corePoolSize * 若是allowCoreThreadTimeout=true,則會直到線程數量=0 5、allowCoreThreadTimeout:容許核心線程超時 6、rejectedExecutionHandler:任務拒絕處理器 * 兩種狀況會拒絕處理任務: - 當線程數已經達到maxPoolSize,切隊列已滿,會拒絕新任務 - 當線程池被調用shutdown()後,會等待線程池裏的任務執行完畢,再shutdown。若是在調用shutdown()和線程池真正shutdown之間提交任務,會拒絕新任務 * 線程池會調用rejectedExecutionHandler來處理這個任務。若是沒有設置默認是AbortPolicy,會拋出異常 * ThreadPoolExecutor類有幾個內部實現類來處理這類狀況: - AbortPolicy 丟棄任務,拋運行時異常 - CallerRunsPolicy 執行任務 - DiscardPolicy 忽視,什麼都不會發生 - DiscardOldestPolicy 從隊列中踢出最早進入隊列(最後一個執行)的任務 * 實現RejectedExecutionHandler接口,可自定義處理器
接下來咱們看下allowCoreThreadTimeOut和keepAliveTime屬性的含義。在壓力很大的狀況下,線程池中的全部線程都在處理新提交的任務或者是在排隊的任務,這個時候線程池處在忙碌狀態。若是壓力很小,那麼可能不少線程池都處在空閒狀態,這個時候爲了節省系統資源,回收這些沒有用的空閒線程,就必須提供一些超時機制,這也是線程池大小調節策略的一部分。經過corePoolSize和maximumPoolSize,控制如何新增線程;經過allowCoreThreadTimeOut和keepAliveTime,控制如何銷燬線程。
allowCoreThreadTimeOut:
該屬性用來控制是否容許核心線程超時退出。默認值爲:false。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。
Runnable getTask() { for (;;) { try { int state = runState; if (state > SHUTDOWN) return null; Runnable r; if (state == SHUTDOWN) // Help drain queue
r = workQueue.poll(); else if (poolSize > corePoolSize || allowCoreThreadTimeOut) r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS); else r = workQueue.take(); if (r != null) return r; if (workerCanExit()) { if (runState >= SHUTDOWN) // Wake up others
interruptIdleWorkers(); return null; } // Else retry
} catch (InterruptedException ie) { // On interruption, re-check runState
} } }
(poolSize > corePoolSize || allowCoreThreadTimeOut)這個條件,就是用來判斷是否容許當前線程退出。workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);就是藉助阻塞隊列,讓空閒線程等待keepAliveTime時間以後,恢復執行。這樣空閒線程會因爲超時而退出。