我們知道,受限於硬件、內存和性能,我們不可能無限制的創建任意數量的線程,因爲每一臺機器允許的最大線程是一個有界值。也就是說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版本。
- public void execute(Runnable command) {
- if (command == null)
- throw new NullPointerException();
- if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
- if (runState == RUNNING && workQueue.offer(command)) {
- if (runState != RUNNING || poolSize == 0)
- ensureQueuedTaskHandled(command);
- }
- else if (!addIfUnderMaximumPoolSize(command))
- reject(command); // is shutdown or saturated
- }
- }
- 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;
- }
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。
- 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時間之後,恢復執行。這樣空閒線程會由於超時而退出。
轉:https://blog.csdn.net/aitangyong/article/details/38822505