1. 程式人生 > >Java執行緒池原理及分析

Java執行緒池原理及分析

執行緒池是很常用的併發框架,幾乎所有需要非同步和併發處理任務的程式都可用到執行緒池。 **使用執行緒池的好處如下**: 1. 降低資源消耗:可重複利用已建立的執行緒池,降低建立和銷燬帶來的消耗; 1. 提高響應速度:任務到達時,可立即執行,無需等待執行緒建立; 1. 提高執行緒的可管理性:執行緒池可對執行緒統一分配、調優和監控。 ## 原理 執行緒池的原理非常簡單,這裡用處理流程來概括: 1. 執行緒池判斷核心池裡的執行緒是否都在執行任務,如果不是,建立一個新的執行緒來執行任務; 1. 如果核心執行緒池已滿,則將新任務存在工作佇列中; 1. 如果工作佇列滿了,執行緒數量沒有達到執行緒池上限的前提下,新建一個執行緒來執行任務; 1. 執行緒數量達到上限,則觸發飽和策略來處理這個任務; 使用工作佇列,是為了儘可能降低執行緒建立的開銷。工作佇列用阻塞佇列來實現。 ### 阻塞佇列 阻塞佇列(BlockingQueue)是指支援阻塞的插入和移除元素的佇列。 - 阻塞的插入:當佇列滿時,阻塞插入元素的執行緒,直到佇列不滿; - 阻塞的移除:當佇列為空,阻塞移除元素的線層,直到佇列不為空; 原理:**使用通知者模式**實現。當生產者往滿的佇列中新增元素時,會阻塞生產者。消費者移除元素時,會通知生產者當前佇列可用。 阻塞佇列有以下三種類型,分別是: - 有界阻塞佇列:ArrayBlockingQueue(陣列),LinkedBlockingQueue(連結串列) - 無界阻塞佇列:LinkedTransferQueue(連結串列),PriorityBlockingQueue(支援優先順序排序),DelayQueue(支援延時獲取元素的無界阻塞佇列) - 同步移交佇列:SynchronousQueue #### 有界阻塞佇列 主要包括ArrayBlockingQueue(陣列),LinkedBlockingQueue(連結串列)兩種。有界佇列大小與執行緒數量大小相互配合,佇列容量大執行緒數量小時,可減少上下文切換降低cpu使用率,但是會降低吞吐量。 #### 無界阻塞佇列 比較常用的是LinkedTransferQueue。FixedThreadPool就是用這個實現的。無界阻塞佇列要慎重使用,因為在某些情況,可能會導致大量的任務堆積到佇列中,導致記憶體飆升。 #### 同步移交佇列 SynchronousQueue。不儲存元素的阻塞佇列,每一個put操作必須等待一個take操作,否則不能繼續新增元素。用於實現CachedThreadPool執行緒池。 各個執行緒池所使用的任務佇列對映關係如下: 執行緒池 | 阻塞佇列 ---|--- FixedThreadPool | LinkedBlockingQueue SingleThreadExecutor | LinkedBlockingQueue CachedThreadExecutor | SynchronousQueue ScheduledThreadPoolExecutor | LinkedBlockingQueue ## 實現類分析 ThreadPoolExecutor是Java執行緒池的實現類,是Executor介面派生出來的最核心的類。依賴關係圖如下: ![image](https://qiniu.debrisflow.cn/20201104Executor.png) 這裡不得不提到Executor框架,該框架包含三大部分,如下: - 任務。被執行任務需要實現的介面:Runnable和Callable; - 任務執行。即上述核心介面Executor以及繼承而來的ExecutorService。ExecutorService派生出如下兩個類: - ThreadPoolExecutor:執行緒池核心實現類; - ScheduledThreadPoolExecutor:用來做定時任務; - 非同步計算的結果。介面Future和實現Future介面的FutureTask類。 ### 執行緒池建立 ``` new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds, runnableTaskQueue, handler) ``` 構造方法如下: ``` public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, Block