1、簡介java
線程池類爲 Java.util.concurrent.ThreadPoolExecutor,經常使用構造方法爲:程序員
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,數據庫
long keepAliveTime, TimeUnit unit,數組
BlockingQueue<Runnable> workQueue,併發
RejectedExecutionHandler handler)異步
corePoolSize: 線程池維護線程的最少數量性能
maximumPoolSize:線程池維護線程的最大數量spa
keepAliveTime: 線程池維護線程所容許的空閒時間.net
unit: 線程池維護線程所容許的空閒時間的單位線程
workQueue: 線程池所使用的緩衝隊列
handler: 線程池對拒絕任務的處理策略
一個任務經過 execute(Runnable)方法被添加到線程池,任務就是一個 Runnable類型的對象,任務的執行方法就是Runnable類型對象的run()方法。
先看一副圖,描述了ThreadPoolExecutor的工做機制:
整個ThreadPoolExecutor的任務處理有4步操做:
幾點說明:(相信這些網上一搜一大把,我這裏簡單介紹下,爲後面作一下鋪墊)
容易被人忽略的點:
1. pool threads啓動後,之後的任務獲取都會經過block queue中,獲取堆積的runnable task.
因此建議: block size >= corePoolSize ,否則線程池就沒任何意義
2. corePoolSize 和 maximumPoolSize的區別, 和你們正常理解的數據庫鏈接池不太同樣。
* 據dbcp pool爲例,會有minIdle , maxActive配置。minIdle表明是常駐內存中的threads數量,maxActive表明是工做的最大線程數。
* 這裏的corePoolSize就是鏈接池的maxActive的概念,它沒有minIdle的概念(每一個線程能夠設置keepAliveTime,超過多少時間多有任務後銷燬線程,但不會固定保持必定數量的threads)。
* 這裏的maximumPoolSize,是一種救急措施的第一層。當threadPoolExecutor的工做threads存在滿負荷,而且block queue隊列也滿了,這時表明接近崩潰邊緣。這時容許臨時起一批threads,用來處理runnable,處理完後立馬退出。
因此建議: maximumPoolSize >= corePoolSize =指望的最大線程數。 (我曾經配置了corePoolSize=1, maximumPoolSize=20, blockqueue爲無界隊列,最後就成了單線程工做的pool。典型的配置錯誤)
3. 善用blockqueue和reject組合. 這裏要重點推薦下CallsRun的Rejected Handler,從字面意思就是讓調用者本身來運行。
咱們常常會在線上使用一些線程池作異步處理,好比我前面作的(業務層)異步並行加載技術分析和設計, 將本來串行的請求都變爲了並行操做,但過多的並行會增長系統的負載(好比軟中斷,上下文切換)。因此確定須要對線程池作一個size限制。可是爲了引入異步操做後,避免因在block queue的等待時間過長,因此須要在隊列滿的時,執行一個callsRun的策略,並行的操做又轉爲一個串行處理,這樣就能夠保證儘可能少的延遲影響。
因此建議: RejectExecutionHandler = CallsRun , blockqueue size = 2 * poolSize (爲啥是2倍poolSize,主要一個考慮就是瞬間高峯處理,容許一個thread等待一個runnable任務)
當一個任務經過execute(Runnable)方法欲添加到線程池時:
l 若是此時線程池中的數量小於corePoolSize,即便線程池中的線程都處於空閒狀態,也要建立新的線程來處理被添加的任務。
l 若是此時線程池中的數量等於 corePoolSize,可是緩衝隊列 workQueue未滿,那麼任務被放入緩衝隊列。
l 若是此時線程池中的數量大於corePoolSize,緩衝隊列workQueue滿,而且線程池中的數量小於maximumPoolSize,建新的線程來處理被添加的任務。
l 若是此時線程池中的數量大於corePoolSize,緩衝隊列workQueue滿,而且線程池中的數量等於maximumPoolSize,那麼經過 handler所指定的策略來處理此任務。也就是:處理任務的優先級爲:核心線程corePoolSize、任務隊列workQueue、最大線程maximumPoolSize,若是三者都滿了,使用handler處理被拒絕的任務。
l 當線程池中的線程數量大於 corePoolSize時,若是某線程空閒時間超過keepAliveTime,線程將被終止。這樣,線程池能夠動態的調整池中的線程數。
unit可選的參數爲java.util.concurrent.TimeUnit中的幾個靜態屬性:
NANOSECONDS、
MICROSECONDS、
MILLISECONDS、
SECONDS。
workQueue經常使用的是:java.util.concurrent.ArrayBlockingQueue
handler有四個選擇:
ThreadPoolExecutor.AbortPolicy()
拋出java.util.concurrent.RejectedExecutionException異常
ThreadPoolExecutor.CallerRunsPolicy()
當拋出RejectedExecutionException異常時,會調用rejectedExecution方法
(若是主線程沒有關閉,則主線程調用run方法,源碼以下
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
)
ThreadPoolExecutor.DiscardOldestPolicy()
拋棄舊的任務
ThreadPoolExecutor.DiscardPolicy()
拋棄當前的任務
2、相關參考
一個 ExecutorService,它使用可能的幾個池線程之一執行每一個提交的任務,一般使用 Executors 工廠方法配置。
線程池能夠解決兩個不一樣問題:因爲減小了每一個任務調用的開銷,它們一般能夠在執行大量異步任務時提供加強的性能,而且還能夠提供綁定和管理資源(包括執行集合任務時使用的線程)的方法。每一個ThreadPoolExecutor 還維護着一些基本的統計數據,如完成的任務數。
爲了便於跨大量上下文使用,此類提供了不少可調整的參數和擴展掛鉤。可是,強烈建議程序員使用較爲方便的 Executors 工廠方法 Executors.newCachedThreadPool()(無界線程池,能夠進行自動線程回收)、Executors.newFixedThreadPool(int)(固定大小線程池)和 Executors.newSingleThreadExecutor()(單個後臺線程),它們均爲大多數使用場景預約義了設置。不然,在手動配置和調整此類時,使用如下指導:
核心和最大池大小
ThreadPoolExecutor 將根據 corePoolSize(參見 getCorePoolSize())和 maximumPoolSize(參見getMaximumPoolSize())設置的邊界自動調整池大小。當新任務在方法 execute(java.lang.Runnable) 中提交時,若是運行的線程少於 corePoolSize,則建立新線程來處理請求,即便其餘輔助線程是空閒的。若是運行的線程多於corePoolSize 而少於 maximumPoolSize,則僅當隊列滿時才建立新線程。若是設置的 corePoolSize 和 maximumPoolSize相同,則建立了固定大小的線程池。若是將 maximumPoolSize 設置爲基本的無界值(如 Integer.MAX_VALUE),則容許池適應任意數量的併發任務。在大多數狀況下,核心和最大池大小僅基於構造來設置,不過也可使用setCorePoolSize(int) 和 setMaximumPoolSize(int) 進行動態更改