看阿里巴巴開發手冊併發編程這塊有一條:線程池不容許使用Executors去建立,而是經過ThreadPoolExecutor的方式,經過源碼分析禁用的緣由
管理一組工做線程。經過線程池複用線程有如下幾點優勢:java
OutOfMemoryError
【簡稱OOM】根據返回的對象類型建立線程池能夠分爲三類:mysql
本文只討論建立返回ThreadPoolExecutor
對象sql
在介紹Executors
建立線程池方法前先介紹一下ThreadPoolExecutor
,由於這些建立線程池的靜態方法都是返回ThreadPoolExecutor
對象,和咱們手動建立ThreadPoolExecutor
對象的區別就是咱們不須要本身傳構造函數的參數。ThreadPoolExecutor
的構造函數共有四個,但最終調用的都是同一個:數據庫
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
構造函數參數說明:編程
執行邏輯說明:設計模式
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
是單線程線程池,只有一個核心線程socket
當一個任務提交時,首先會建立一個核心線程來執行任務,若是超過核心線程的數量,將會放入隊列中,由於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. 日誌框架,選擇Logback Or Log4j2?
2. Logback配置文件這麼寫,TPS提升10倍
工程相關:
1. 閒來無事,動手寫一個LRU本地緩存
2. Redis實現點贊功能模塊
3. JMX可視化監控線程池
4. 權限管理 【SpringSecurity篇】
5. Spring自定義註解從入門到精通
6. java模擬登錄優酷
7. QPS這麼高,那就來寫個多級緩存吧
8. java使用phantomjs進行截圖