怎么才算掌握了JDK中的執行緒池
JDK并發包下面的執行緒池是面試中經常被考查的點,之前我寫過一篇ThreadPoolExecutor原始碼分析的文章,因為篇幅有限當時沒說面試中常見的考查點和哪些點是應該掌握,那篇文章著實有點長,更合適用電腦看,結合原始碼看,今天,我來談談自己覺得ThreadPoolExecutor哪些點是應該掌握的,這些點應該掌握的點正是面試中經常被問的東西,現在拋出幾個問題,如果你都能答上來,可以不用往下面看啦,
- ThreadPoolExecutor中常用引數有哪些,作用是什么?任務提交后,ThreadPoolExecutor會按照什么策略去創建執行緒用于執行提交任務?
- ThreadPoolExecutor有哪些狀態,狀態之間流轉是什么樣子的?
- ThreadPoolExecutor中的執行緒哪個時間點被創建?是任務提交后嗎?可以在任務提交前創建嗎?
- ThreadPoolExecutor中創建的執行緒哪個時間被啟動?
- ThreadPoolExecutor竟然是執行緒池那么他是如何做到重復利用執行緒的?
- ThreadPoolExecutor中創建的同一個執行緒同一時刻能執行多個任務嗎?如果不能是通過什么機制保證ThreadPoolExecutor中的同一個執行緒只能執行完一個任務,才會機會去執行另一個任務?
- ThreadPoolExecutor中關閑執行緒池的方法shutdown與shutdownNow的區別是什么?
- 通過submit方法向ThreadPoolExecutor提交任務后,當所有的任務都執行完后不呼叫shutdown或shutdownNow方法會有問題嗎?
- ThreadPoolExecutor有沒有提供擴展點,方便在任務執行前或執行后做一些事情?
如果回答的上就pass吧,哈哈
ThreadPoolExecutor引數有哪些與創建執行緒策略?
ThreadPoolExecutor引數
corePoolSize執行緒池中的核心執行緒數mmaximumPoolSize執行緒池中的最大執行緒數keepAliveTime當執行緒池中執行緒數量超過corePoolSize時,允許等待多長時間從workQueue中拿任務unitkeepAliveTime 對應的時間單位,為TimeUnit類,workQueue阻塞佇列,當執行緒池中執行緒數超過corePoolSize時,用于存盤提交的任務,threadFactory執行緒池采用,該執行緒工廠創建執行緒池中的執行緒,handler為RejectedExecutionHandler,當執行緒線中執行緒超過maximumPoolSize時采用的,拒絕執行處理器,
創建執行緒策略
簡單介紹一下,一個任務提交給執行緒池后,執行緒池創建執行緒來執行提交任務的流程,
1、當提交任務時執行緒池中的來用執行任務的執行緒數小于corePoolSize(核心執行緒數),則執行緒池利用ThreadFacory(執行緒工廠)創建執行緒用于執行提交的任務,否則執行第二2步,
2、當提交任務時執行緒池中的來用執行任務的執行緒數大于corePoolSize(核心執行緒數),但workQueue沒有滿,則執行緒池會將提交的任務先保存在workQueue(作業佇列),等待執行緒池中的執行緒執行完其它已提交任務后會回圈從workQueue中取出任務執行,否則執行第3步,
3、當提交任務時執行緒池中的來用執行任務大于corePoolSize(核心執行緒數),且workQueu已滿,但沒有超過maximunPoolSize(最大執行緒數),則執行緒池利用ThreadFacory(執行緒工廠)創建執行緒用于執行提交的任務,否則執行4,
4、當提交任務時執行緒池中的來用執行任務大于maximunPoolSize,執行執行緒池中配置的拒絕策略(RejectedExecutionHanlder),
所以在設定ThreadPoolExecutor的引數時一定要特別小心,不建議采用很大的ArrayBlockQueue或不限大小的LinkedBlockQueue,同時corePoolSize也不應該設定過大,CUP密集的任務的話可以設定小一點(CUP資料+1這種)避免不必要的背景關系切換;而對于IO密集的任務則corePoolSize則可以設定的大一點,可以避免長時間IO等待而CUP卻空閑,threadFactory建議采用自己定義的,讓其創建的執行緒容易區分,方便問題定位,
執行緒池有哪些狀態,狀態之間流轉是什么樣子的?
- RUNNING:運行中,接收新的任務或處理佇列中的任務,
- SHUTDOWN:關閉,不再接收新的任務,但會處理佇列中的任務值為0,
- STOP:停止,不再接收新的任務,也不處理佇列中的任務,并中斷正在處理的任務,
- TIDYING:所有任務已結束佇列大小為0,轉變TIDYING狀態的執行緒將會執行terminated()方法,
- TERMINATED:結束terminated()已被執行完,
狀態流程如下圖:
池程池中的執行緒哪個時間點被創建?
ThreadPoolExecutor中的執行緒哪個時間點被創建?是任務提交后嗎?可以在任務提交前創建嗎?
一般在任務被提交后,執行緒池會利用執行緒工廠去創建執行緒,但當執行緒池中執行緒數已為corePoolSize時或maxmumPoolSize時不會,可以在任務提交前通過prestartCoreThread方法或prestartAllCoreThreads方法預先創建核心執行緒,具體可以參考這下這個圖:
ThreadPoolExecutor中創建的執行緒哪個時間被啟動?
執行緒池中執行緒實作是在addWorker方法中被創建的,詳見之前文章中addWorker方法分析,創建后完,該執行緒就被啟動,執行緒池中被創建的執行緒被封裝到了Worker物件中,而Worker類又實作了Runnable介面,執行緒池中的執行緒又參考了worker,當執行緒被start后實際就有機會等待作業系統調度執行Worker類的run方法,
Worker(Runnable firstTask) {
setState(-1);
this.firstTask = firstTask;
//創建的執行緒參考了worker
this.thread = getThreadFactory().newThread(this);
}
ThreadPoolExecutor竟然是執行緒池那么他是如何做到重復利用執行緒的?
一旦執行緒池通過ThreadFactory創建好執行緒后,就會將創建的執行緒封裝到了Worker物件中,同時啟動該執行緒,新創建的執行緒會執行剛提交的任務,同時會不斷地從workerQueue中取出任務執行,執行緒池的執行緒復用正是通過不斷地從workerQueue中取出任務來執行達到的,原始碼分析見runWorkers方法分析,
ThreadPoolExecutor中創建的同一個執行緒同一時刻能執行多個任務嗎?
同時一時刻不能執行多個任務,只有一個任務執行完時才能去執行另一個任務,上面說到執行緒池中通過ThreadFacory創建的執行緒最后會被封裝到Worker中,而該執行緒又參考了Worker,start執行緒后,任務其實是在Worker中的run方法中被執行,最終run又將任務執行代理給ThreadPoolExecutor的runWorker方法,
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{...}
Worder一方面實作了Runnable,另一方面又繼承了AQS,通過實作AQS,Worker具有了排它鎖的語意,每次在執行提交任務時都會先lock操作,執行完任務后再做unlock操作,正是這個加鎖與解鎖的操作,保證了同一個執行緒要執行完當前任務才有機再去執行另一個任務,
ThreadPoolExecutor中關閑執行緒池的方法shutdown與shutdownNow的區別是什么?
shutdown方法是將執行緒池的狀態設定為SHUTDOWN,此時新任務不能被提交(提交會拋出例外),workerQueue的任務會被繼續執行,同時執行緒池會向那些空閑的執行緒發出中斷信號,空閑的執行緒實際就不沒在執行任務的執行緒,如何被封裝在worker里的執行緒能加鎖,這里這個執行緒實作會就空閑的,下面是向空閑的執行緒發出中斷信號原始碼,
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
//w.tryLock()用于加鎖,看執行緒是否在執行任務
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
shutdownNow方法是將執行緒池的狀態設定為STOP,此時新任務不能被提交(提交會拋出例外),執行緒池中所有執行緒都會收到中斷的信號,具體執行緒會作出什么回應,要看情況,如果執行緒因為呼叫了Object的wait、join方法或是自身的sleep方法而阻塞,那么中斷狀態會被清除,同時拋出InterruptedException,其它情況可以參考Thread.interrupt方法的說明,shutdownNow方法向所有執行緒發出中斷資訊原始碼如下:
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
//加鎖操作保證中斷程序中不會新woker被創建
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
通過submit方法向ThreadPoolExecutor提交任務后,當所有的任務都執行完后不呼叫shutdown或shutdownNow方法會有問題嗎?
如果沒指核心執行緒允許超時將會有問題,核心執行緒允許超時是指在從wokerQueue中獲取任務時,采用的阻塞的獲取方式等待任務到來,還是通過設定超時的方式從同步阻塞佇列中獲取任務,即是通通過BlockingQueue的poll方法獲取任務還是take方法獲取任務,可參考之前的原始碼分析中的getTask方法分析,如果不呼叫shutdown或shutdownNow方法,核心執行緒由于在getTask方法呼叫BlockingQueue.take方法獲取任務而處于一直被阻塞掛起狀態,核心執行緒將永遠處于Blocking的狀態,導致記憶體泄漏,主執行緒也無法退出,除非強制kill,試著運行如下程式會發現,程式無法退出,
public class Test {
public static void main(String args[]) {
ExecutorService executorService = new ThreadPoolExecutor(3, 3,10L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("thread name " + Thread.currentThread().getName());
}
});
}
}
所在使用執行緒池時一定要記得根本具體場景呼叫shutdown或shutdownNow方法關閉執行緒池,shutdown方法適用于提交任務都要被執行完的場景,shutdownNow方法適用于不關心提交任務是否執行完的場景,
ThreadPoolExecutor有沒有提供擴展點,方便在任務執行前或執行后做一些事情?
執行緒池提供了三個擴展點,分別是提交任務的run方法或是call方法被呼叫前與被調后,即beforeExecutor與afaterExecutor方法;另外一個擴展點是執行緒池的狀態從TIDYING狀態流轉為TERMINATED狀態時terminated方法會被呼叫,
總結
本來只是想寫一點點,寫著寫著就發現又有點長,這篇主要是介紹了ThreadPoolExecutor中個人認為比較重要點,同時也是把ThreadPoolExecutor再梳理一下發現自己之前理解有偏差的地方,
看完三件事??
如果你覺得這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:
-
點贊,轉發,有你們的 『點贊和評論』,才是我創造的動力,
-
關注公眾號 『 java爛豬皮 』,不定期分享原創知識,
-
同時可以期待后續文章ing??
作者:葉易
出處:club.perfma.com/article/191…
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/160283.html
標籤:Java
