原文地址: http://blog.jboost.cn/threadpool.html
本文內容是基於研發部門內部的分享整理,記錄下來供學習或回顧。
1. 為什么要用線程池
-
降低資源消耗。通過重復利用已創建的線程降低線程創建、銷毀線程造成的消耗。
-
提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。
-
提高線程的可管理性。線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配、調優和監控
2. ThreadPoolExecutor線程池類參數詳解
參數 | 說明 |
---|---|
corePoolSize | 核心線程數量,線程池維護線程的最少數量 |
maximumPoolSize | 線程池維護線程的最大數量 |
keepAliveTime | 線程池除核心線程外的其他線程的最長空閑時間,超過該時間的空閑線程會被銷毀 |
unit | keepAliveTime的單位,TimeUnit中的幾個靜態屬性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS |
workQueue | 線程池所使用的任務緩沖隊列 |
threadFactory | 線程工廠,用於創建線程,一般用默認的即可 |
handler | 線程池對拒絕任務的處理策略 |
當線程池任務處理不過來的時候(什么時候認為處理不過來后面描述),可以通過handler指定的策略進行處理,ThreadPoolExecutor提供了四種策略:
- ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常;也是默認的處理方式。
- ThreadPoolExecutor.DiscardPolicy:丟棄任務,但是不拋出異常。
- ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新嘗試執行任務(重復此過程)
- ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
可以通過實現RejectedExecutionHandler接口自定義處理方式。
3. 線程池任務執行
3.1. 添加執行任務
- submit() 該方法返回一個Future對象,可執行帶返回值的線程;或者執行想隨時可以取消的線程。Future對象的get()方法獲取返回值。Future對象的cancel(true/false)取消任務,未開始或已完成返回false,參數表示是否中斷執行中的線程
- execute() 沒有返回值。
3.2. 線程池任務提交過程
一個線程提交到線程池的處理流程如下圖
- 如果此時線程池中的數量小於corePoolSize,即使線程池中的線程都處於空閑狀態,也要創建新的線程來處理被添加的任務。
- 如果此時線程池中的數量等於corePoolSize,但是緩沖隊列workQueue未滿,那么任務被放入緩沖隊列。
- 如果此時線程池中的數量大於等於corePoolSize,緩沖隊列workQueue滿,並且線程池中的數量小於maximumPoolSize,建新的線程來處理被添加的任務。
- 如果此時線程池中的數量大於corePoolSize,緩沖隊列workQueue滿,並且線程池中的數量等於maximumPoolSize,那么通過 handler所指定的策略來處理此任務。
- 當線程池中的線程數量大於 corePoolSize時,如果某線程空閑時間超過keepAliveTime,線程將被終止。這樣,線程池可以動態的調整池中的線程數。
總結即:處理任務判斷的優先級為 核心線程corePoolSize、任務隊列workQueue、最大線程maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務。
注意:
- 當workQueue使用的是無界限隊列時,maximumPoolSize參數就變的無意義了,比如new LinkedBlockingQueue(),或者new ArrayBlockingQueue(Integer.MAX_VALUE);
- 使用SynchronousQueue隊列時由於該隊列沒有容量的特性,所以不會對任務進行排隊,如果線程池中沒有空閑線程,會立即創建一個新線程來接收這個任務。maximumPoolSize要設置大一點。
- 核心線程和最大線程數量相等時keepAliveTime無作用.
3.3. 線程池關閉
- shutdown() 不接收新任務,會處理已添加任務
- shutdownNow() 不接受新任務,不處理已添加任務,中斷正在處理的任務
4. 常用隊列介紹
- ArrayBlockingQueue: 這是一個由數組實現的容量固定的有界阻塞隊列.
- SynchronousQueue: 沒有容量,不能緩存數據;每個put必須等待一個take; offer()的時候如果沒有另一個線程在poll()或者take()的話返回false。
- LinkedBlockingQueue: 這是一個由單鏈表實現的默認無界的阻塞隊列。LinkedBlockingQueue提供了一個可選有界的構造函數,而在未指明容量時,容量默認為Integer.MAX_VALUE。
隊列操作:
方法 | 說明 |
---|---|
add | 增加一個元索; 如果隊列已滿,則拋出一個異常 |
remove | 移除並返回隊列頭部的元素; 如果隊列為空,則拋出一個異常 |
offer | 添加一個元素並返回true; 如果隊列已滿,則返回false |
poll | 移除並返回隊列頭部的元素; 如果隊列為空,則返回null |
put | 添加一個元素; 如果隊列滿,則阻塞 |
take | 移除並返回隊列頭部的元素; 如果隊列為空,則阻塞 |
element | 返回隊列頭部的元素; 如果隊列為空,則拋出一個異常 |
peek | 返回隊列頭部的元素; 如果隊列為空,則返回null |
5. Executors線程工廠類
-
Executors.newCachedThreadPool();
說明: 創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程.
內部實現:new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue()); -
Executors.newFixedThreadPool(int);
說明: 創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。
內部實現:new ThreadPoolExecutor(nThreads, nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue()); -
Executors.newSingleThreadExecutor();
說明:創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照順序執行。
內部實現:new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue()) -
Executors.newScheduledThreadPool(int);
說明:創建一個定長線程池,支持定時及周期性任務執行。
內部實現:new ScheduledThreadPoolExecutor(corePoolSize)
【附】阿里巴巴Java開發手冊中對線程池的使用規范
-
【強制】創建線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。
正例:public class TimerTaskThread extends Thread { public TimerTaskThread(){ super.setName("TimerTaskThread"); ... } }
-
【強制】線程資源必須通過線程池提供,不允許在應用中自行顯式創建線程。
說明: 使用線程池的好處是減少在創建和銷毀線程上所花的時間以及系統資源的開銷,解決資
源不足的問題。如果不使用線程池,有可能造成系統創建大量同類線程而導致消耗完內存或者
“過度切換”的問題。 - 【強制】線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣
的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。
說明: Executors 返回的線程池對象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允許的請求隊列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。
2) CachedThreadPool 和 ScheduledThreadPool:
允許的創建線程數量為 Integer.MAX_VALUE, 可能會創建大量的線程,從而導致 OOM。
6. 總結
ThreadPoolExecutor通過幾個核心參數來定義不同類型的線程池,適用於不同的使用場景;其中在任務提交時,會依次判斷corePoolSize, workQueque, 及maximumPoolSize,不同的狀態不同的處理。技術領域水太深,如果不是日常使用,基本一段時間后某些知識點就忘的差不多了,因此階段性地回顧與總結,對夯實自己的技術基礎很有必要。
我的個人博客地址:http://blog.jboost.cn
我的微信公眾號:jboost-ksxy (一個不只有技術干貨的公眾號,歡迎關注,及時獲取更新內容)
————————————————————————————————————