包 java.util.concurrent.*
[ 一 ]、常用線程池
最常用構造方法為:
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
JDK內建的配置好的線程池:
// 固定背景工作執行緒數量的線程池
ExecutorService executorService1 = Executors.newFixedThreadPool(3);
// 一個可快取的線程池
ExecutorService executorService2 = Executors.newCachedThreadPool();
// 單線程化的Executor
ExecutorService executorService3 = Executors.newSingleThreadExecutor();
// 支援定時的以及周期性的任務執行
ExecutorService executorService4 = Executors.newScheduledThreadPool(3);
這些預定義好的線程池服務也是基於ThreadPoolExecutor配置的,所以我們應該從最基本的參數著手瞭解,如下:
參數詳細說明:
[ 1 ]、corePoolSize: 線程池維護線程的最少數量
[ 2 ]、maximumPoolSize:線程池維護線程的最大數量
[ 3 ]、keepAliveTime: 線程池維護線程所允許的空閑時間
[ 4 ]、unit: 線程池維護線程所允許的空閑時間的單位,unit可選的參數為java.util.concurrent.TimeUnit中的幾個靜態屬性:
- NANOSECONDS
- MICROSECONDS
- MILLISECONDS
- SECONDS
[ 5]、 workQueue: 線程池所使用的緩衝隊列,常用的是:java.util.concurrent.ArrayBlockingQueue
[6 ] threadFactory 負責給線程池建立線程的工廠。線程池中的線程是由ThreadFactory建立。如果不特別指定,會使用Executors.defaultThreadFactory建立位於同一個線程組,相同優先順序(NORM_PRIORITY)的非守護線程。如果由你來指定ThreadFactory,你可以定製線程名字,線程組,優先順序,是否為守護線程等屬性。
[ 7 ]、 handler: 線程池對拒絕任務的處理策略,有四個選擇如下:
- ThreadPoolExecutor.AbortPolicy():拋出java.util.concurrent.RejectedExecutionException異常
- ThreadPoolExecutor.CallerRunsPolicy():重試添加當前的任務,他會自動重複調用execute()方法
- ThreadPoolExecutor.DiscardOldestPolicy():拋棄舊的任務
- ThreadPoolExecutor.DiscardPolicy():拋棄當前的任務
[ 二 ]、詳細說明
[ 1 ]、當一個任務通過execute(Runnable)方法欲添加到線程池時:
- 如果此時線程池中的數量小於corePoolSize,即使線程池中的線程都處於空閑狀態,也要建立新的線程來處理被添加的任務。
- 如果此時線程池中的數量等於 corePoolSize,但是緩衝隊列 workQueue未滿,那麼任務被放入緩衝隊列。
- 如果此時線程池中的數量大於corePoolSize,緩衝隊列workQueue滿,並且線程池中的數量小於maximumPoolSize,建新的線程來處理被添加的任務。
- 如果此時線程池中的數量大於corePoolSize,緩衝隊列workQueue滿,並且線程池中的數量等於 maximumPoolSize,那麼通過 handler所指定的策略來處理此任務。也就是:處理任務的優先順序為:核心線程corePoolSize、任務隊列workQueue、最大線程 maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務。
- 當線程池中的線程數量大於 corePoolSize時,如果某線程空閑時間超過keepAliveTime,線程將被終止。這樣,線程池可以動態調整池中的線程數。
ThreadPoolExecutor的工作機制:
整個ThreadPoolExecutor的任務處理有4步操作:
- 第一步,初始的poolSize < corePoolSize,提交的runnable任務,會直接做為new一個Thread的參數,立馬執行
- 第二步,當提交的任務數超過了corePoolSize,就進入了第二步操作。會將當前的runable提交到一個block queue中
- 第三步,如果block queue是個有界隊列,當隊列滿了之後就進入了第三步。如果poolSize < maximumPoolsize時,會嘗試new 一個Thread的進行救急處理,立馬執行對應的runnable任務
- 第四步,如果第三步救急方案也無法處理了,就會走到第四步執行reject操作。
幾點說明:(相信這些網上一搜一大把,我這裡簡單介紹下,為後面做一下鋪墊)
- block queue有以下幾種實現:
1. ArrayBlockingQueue : 有界的數組隊列
2. LinkedBlockingQueue : 可支援有界/無界的隊列,使用鏈表實現
3. PriorityBlockingQueue : 優先隊列,可以針對任務排序
4. SynchronousQueue : 隊列長度為1的隊列,和Array有點區別就是:client thread提交到block queue會是一個阻塞過程,直到有一個worker thread串連上來poll task。
- RejectExecutionHandler是針對任務無法處理時的一些自保護處理:
1. Reject 直接拋出Reject exception
2. Discard 直接忽略該runnable,不可取
3. DiscardOldest 丟棄最早入隊列的的任務
4. CallsRun 直接讓原先的client thread做為worker線程,進行執行
容易被人忽略的點: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任務) 參考:http://www.iteye.com/topic/1118660 更多:http://blog.csdn.net/waterbig/article/details/4794244http://rdc.taobao.com/team/jm/archives/595