多執行緒——Java執行緒池原理深入
上次我們簡單瞭解了一下什麼是執行緒池以及Java中幾種型別的執行緒池,今天我們來深入剖析一下執行緒池的原理。
1、構造
1. 執行緒池管理器(ThreadPoolManager):用於建立並管理執行緒池
2. 工作執行緒(WorkThread): 執行緒池中執行緒
3. 任務介面(Task):每個任務必須實現的介面,以供工作執行緒排程任務的執行。
4. 任務佇列:用於存放沒有處理的任務。提供一種緩衝機制。
注:執行緒池管理器至少有下列功能:建立執行緒池,銷燬執行緒池,新增新任務
2、執行緒狀態
新建狀態(New):新建立了一個執行緒物件。
就緒狀態(Runnable):執行緒物件建立後,其他執行緒呼叫了該物件的start()方法。該狀態的執行緒位於可執行執行緒池中,變得可執行,等待獲取CPU的使用權。
執行狀態(Running):就緒狀態的執行緒獲取了CPU,執行程式程式碼。
阻塞狀態(Blocked):阻塞狀態是執行緒因為某種原因放棄CPU使用權,暫時停止執行。直到執行緒進入就緒狀態,才有機會轉到執行狀態。阻塞的情況分三種:
(一)、等待阻塞:執行的執行緒執行wait()方法,JVM會把該執行緒放入等待池中。
(二)、同步阻塞:執行的執行緒在獲取物件的同步鎖時,若該同步鎖被別的執行緒佔用,則JVM會把該執行緒放入鎖池中。
(三)、其他阻塞:執行的執行緒執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該執行緒置為阻塞狀態。當sleep()狀態超時、join()等待執行緒終止或者超時、或者I/O處理完畢時,執行緒重新轉入就緒狀態。
死亡狀態(Dead):執行緒執行完了或者因異常退出了run()方法,該執行緒結束生命週期。
3、執行流程
a.一個任務提交,如果執行緒池大小沒達到corePoolSize,則每次都啟動一個worker也就是一個執行緒來立即執行
b.如果來不及執行,則把多餘的執行緒放到workQueue,等待已啟動的worker來迴圈執行
c.如果佇列workQueue都放滿了還沒有執行,則在maximumPoolSize下面啟動新的worker來迴圈執行workQueue
d.如果啟動到maximumPoolSize還有任務進來,執行緒池已達到滿負載,此時就執行任務拒絕RejectedExecutionHandler
// 流程就是:沒達到corePoolSize,建立worker執行,達到corePoolSize加入workQueue
// workQueue滿了且在maximumPoolSize下,建立新worker,達到maximumPoolSize,執行reject
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
// 1:poolSize達到corePoolSize,執行3把任務加入workQueue
// 2:poolSize沒達到,執行addIfUnderCorePoolSize()在corePoolSize內建立新worker立即執行任務
// 如果達到corePoolSize,則同上執行3
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
// 3:workQueue滿了,執行5
if (runState == RUNNING && workQueue.offer(command)) {
if (runState != RUNNING || poolSize == 0) {
// 4:如果執行緒池關閉,執行拒絕策略
// 如果poolSize==0,新啟動一個執行緒執行佇列內任務
ensureQueuedTaskHandled(command);
}
// 5:在maximumPoolSize內建立新worker立即執行任務
// 如果達到maximumPoolSize,執行6拒絕策略
} else if (!addIfUnderMaximumPoolSize(command))
// 6:拒絕策略
reject(command); // is shutdown or saturated
}
}
4、執行緒銷燬
keepAliveTime:代表的就是執行緒空閒後多久後銷燬,執行緒的銷燬是通過worker的getTask()來實現的。
一般來說,Worker會迴圈獲取getTask(),如果getTask()返回null則工作執行緒worker終結,那我們再看看什麼時候getTask()返回null
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)
// 在poolSize大於corePoolSize或允許核心執行緒超時時
// 阻塞超時獲取有可能獲取到null,此時worker執行緒銷燬
r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
else
r = workQueue.take();
if (r != null)
return r;
// 這裡是是否執行worker執行緒銷燬的判斷
if (workerCanExit()) {
if (runState >= SHUTDOWN)
// STOP或TERMINATED狀態,終止空閒worker
interruptIdleWorkers();
return null; // 這裡返回null,代表工作執行緒worker銷燬
}
// 其他:retry,繼續迴圈
} catch (InterruptedException ie) {
// On interruption, re-check runState
}
}
}
5、執行緒池關閉
平緩關閉 shutdown:這個方法會將runState置為SHUTDOWN,會終止所有空閒的執行緒,同時不再接受新的任務,而仍在工作的執行緒不受影響,所以佇列中的任務人會被執行
立即關閉 shutdownNow:此方法將runState置為STOP,和shutdown方法的區別是,這個方法會終止所有的執行緒(取消所有正在執行和未執行的任務),所以佇列中的任務也不會被執行了。
總結:
通過這次學習,我們不僅知道了執行緒池是如何管理執行緒,而且還了解了執行緒多種狀態之間的轉換,這樣更加便於我們對執行緒池的理解。之後,我們還將繼續學習關於多執行緒的知識,讓我們每天都有成長。