一、爲什麼要使用線程池
- 降低資源消耗。通過重複利用已創建的線程降低線程創建和銷燬造成的銷耗。
- 提高響應速度。當任務到達時,任務可以不需要等待線程創建就能立即執行。
- 提高線程的可管理性。線程是稀缺資源,如果無限制的創建,不僅會銷耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控。
- 線程池的三個特點
- 線程複用
- 控制最大併發數
- 線程管理
二、線程池的使用
Executor類圖
基本使用
參考博文JUC之創建線程的四種方式
三、底層原理
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
三種創建線程池的方式都是通過底層調用ThreadPoolExecutor類來實現的。
ThreadPoolExecutor類的參數解析
- int corePoolSize
線程池中常駐核心線程數。 - int maximumPoolSize
線程池中能夠容納同時執行的最大線程數,大於等於corePoolSize。 - long keepAliveTime
空閒線程的存活時間,超過corePoolSize指定數量的空閒線程,當空閒時間達到keepAliveTime將會被銷燬。 - TimeUnit unit
keepAliveTime的單位。 - BlockingQueue workQueue
任務隊列,被提交但尚未被執行的任務。
newFixedThreadPool 和 newSingleThreadExecutor默認是LinkedBlockingQueue;
newCachedThreadPool 默認是 SynchronousQueue - ThreadFactory threadFactory
用於線程池中創建線程,一般保持默認即可。 - RejectedExecutionHandler handler
拒絕策略,表示當隊列滿了,並且工作線程等於線程池能容納的最大線程數時,如何拒絕新提交的任務請求。
- AbortPolicy(默認的的拒絕策略)
直接拋出RejectedExecutionException異常 - CallerRunsPolicy
哪個線程(調用execute)提交過來的,就還回去給誰(哪來的回哪去),如果提交的線程shutdown了,就將其丟棄 - DiscardPolicy
直接丟棄 - DiscardOldestPolicy
等待最久的先丟棄
四、線程池的處理流程
- 在創建了線程池後,開始等待請求。
- 當調用execute()方法添加一個請求任務時,線程池會做出如下判斷:
- 如果正在運行的線程數量小於corePoolSize,那麼馬上創建線程運行這個任務;
- 如果正在運行的線程數量大於或等於corePoolSize,那麼將這個任務放入隊列;
- 如果這個時候隊列滿了且正在運行的線程數量還小於maximumPoolSize,那麼還是要創建非核心線程立刻運行這個任務;
- 如果隊列滿了且正在運行的線程數量大於或等於maximumPoolSize,那麼線程池會啓動飽和拒絕策略來執行。
- 當一個線程完成任務時,它會從隊列中取下一個任務來執行。
- 當一個線程無事可做超過一定的時間(keepAliveTime)時,線程會判斷:
如果當前運行的線程數大於corePoolSize,那麼這個線程就被停掉。
所以線程池的所有任務完成後,它最終會收縮到corePoolSize的大小。