看阿里巴巴開發手冊併發編程這塊有一條:線程池不容許使用Executors去建立,而是經過ThreadPoolExecutor的方式,經過源碼分析禁用的緣由java
首先感謝你們在蓋樓的間隙閱讀本篇文章,經過閱讀本篇文章你將瞭解到:mysql
若是隻想知道緣由能夠直接拉到總結那sql
管理一組工做線程。經過線程池複用線程有如下幾點優勢:數據庫
OutOfMemoryError
【簡稱OOM】根據返回的對象類型建立線程池能夠分爲三類:編程
本文只討論建立返回ThreadPoolExecutor
對象設計模式
在介紹Executors
建立線程池方法前先介紹一下ThreadPoolExecutor
,由於這些建立線程池的靜態方法都是返回ThreadPoolExecutor
對象,和咱們手動建立ThreadPoolExecutor
對象的區別就是咱們不須要本身傳構造函數的參數。ThreadPoolExecutor
的構造函數共有四個,但最終調用的都是同一個:緩存
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
複製代碼
構造函數參數說明:bash
corePoolSize
參數有關,未滿則建立線程執行任務workQueue
參數有關,若未滿則加入隊列中maximumPoolSize
參數有關,若未滿建立線程執行任務handler
參數有關Executors
建立返回ThreadPoolExecutor對象的方法共有三種:併發
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
複製代碼
CachedThreadPool
是一個根據須要建立新線程的線程池框架
當一個任務提交時,corePoolSize
爲0不建立核心線程,SynchronousQueue
是一個不存儲元素的隊列,能夠理解爲隊裏永遠是滿的,所以最終會建立非核心線程來執行任務。對於非核心線程空閒60s時將被回收。由於Integer.MAX_VALUE
很是大,能夠認爲是能夠無限建立線程的,在資源有限的狀況下容易引發OOM異常
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
複製代碼
SingleThreadExecutor
是單線程線程池,只有一個核心線程
當一個任務提交時,首先會建立一個核心線程來執行任務,若是超過核心線程的數量,將會放入隊列中,由於LinkedBlockingQueue
是長度爲Integer.MAX_VALUE
的隊列,能夠認爲是無界隊列,所以往隊列中能夠插入無限多的任務,在資源有限的時候容易引發OOM
異常,同時由於無界隊列,maximumPoolSize
和keepAliveTime
參數將無效,壓根就不會建立非核心線程
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
複製代碼
FixedThreadPool
是固定核心線程的線程池,固定核心線程數由用戶傳入
SingleThreadExecutor
相似,惟一的區別就是核心線程數不一樣,而且因爲使用的是LinkedBlockingQueue
,在資源有限的時候容易引發OOM
異常OOM
異常OOM
異常這就是爲何禁止使用Executors
去建立線程池,而是推薦本身去建立ThreadPoolExecutor
的緣由
理論上會出現OOM
異常,必須測試一波驗證以前的說法: 測試類:TaskTest.java
public class TaskTest {
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
int i = 0;
while (true) {
es.submit(new Task(i++));
}
}
}
複製代碼
使用Executors
建立的CachedThreadPool
,往線程池中無限添加線程 在啓動測試類以前先將JVM
內存調整小一點,否則很容易將電腦跑出問題【別問我爲何知道,是鐵憨憨甜沒錯了!!!】,在idea
裏:Run
-> Edit Configurations
JVM
參數說明:
Java Heap
內存初始化值Java Heap
內存最大值運行結果:
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
Disconnected from the target VM, address: '127.0.0.1:60416', transport: 'socket'
複製代碼
建立到3w多個線程的時候開始報OOM
錯誤
另外兩個線程池就不作測試了,測試方法一致,只是建立的線程池不同
CPU
數量 + 1,CPU
數量能夠根據Runtime.availableProcessors
方法獲取CPU
數量 * CPU
利用率 * (1 + 線程等待時間/線程CPU時間)CPU
密集型和IO
密集型,而後分別使用不一樣的線程池去處理,從而使每一個線程池能夠根據各自的工做負載來調整AbortPolicy
拒絕策略,直接在程序中拋出RejectedExecutionException
異常【由於是運行時異常,不強制catch
】,這種處理方式不夠優雅。處理拒絕策略有如下幾種比較推薦:
RejectedExecutionException
異常,在捕獲異常中對任務進行處理。針對默認拒絕策略CallerRunsPolicy
拒絕策略,該策略會將任務交給調用execute的線程執行【通常爲主線程】,此時主線程將在一段時間內不能提交任何任務,從而使工做線程處理正在執行的任務。此時提交的線程將被保存在TCP
隊列中,TCP隊列滿將會影響客戶端,這是一種平緩的性能下降RejectedExecutionHandler
接口便可DiscardPolicy
和DiscardOldestPolicy
拒絕策略將任務丟棄也是能夠的若是使用Executors的靜態方法建立ThreadPoolExecutor
對象,能夠經過使用Semaphore
對任務的執行進行限流也能夠避免出現OOM
異常
因爲線程池參數定義經驗較少,都是理論知識,歡迎有經驗的大佬補充
天天學習一點點,加油呀,歡迎點贊
設計模式相關:
1. 單例模式,你真的寫對了嗎?
2. (策略模式+工廠模式+map)套餐 Kill 項目中的switch case
JAVA8相關:
1. 使用Stream API優化代碼
2. 親,建議你使用LocalDateTime而不是Date哦
數據庫相關:
1. mysql數據庫時間類型datetime、bigint、timestamp的查詢效率比較
2. 很高興!終於踩到了慢查詢的坑
高效相關:
1. 擼一個Java腳手架,一統團隊項目結構風格
日誌相關:
1. 日誌框架,選擇Logback Or Log4j2?
2. Logback配置文件這麼寫,TPS提升10倍
工程相關:
1. 閒來無事,動手寫一個LRU本地緩存
2. Redis實現點贊功能模塊
3. JMX可視化監控線程池
4. 權限管理 【SpringSecurity篇】
5. Spring自定義註解從入門到精通
6. java模擬登錄優酷
7. QPS這麼高,那就來寫個多級緩存吧
8. java使用phantomjs進行截圖