在jdk中爲咱們提供了三種建立線程池的方式,可是在阿里的編碼規範裏面都是明確禁止使用這三種api去建立線程池,推薦咱們去自定義線程池。爲何?api
要回答爲何,咱們須要明白建立線程池時,各參數的做用:緩存
首先咱們來看一下jdk提供的建立線程池的三個api:編碼
1. newFixedThreadPool 建立固定數量線程的線程池。spa
2. newSingleThreadExecutor 建立單線程的線程池線程
3. newCachedThreadPool 建立一個帶有緩存的線程池日誌
發現這幾種建立線程池的api,實質上都是依賴於ThreadPoolExecutor類來建立線程池。code
那咱們來看一下ThreadPoolExecutor 建立線程池時須要的參數,以及其做用。blog
建立一個線程池,須要7個參數。隊列
1. corePoolSize: 線程池的核心線程數量。初始是不建立線程的。當有任務提交到線程池時,斷定若是已經建立的線程數量小於核心數量,且沒有空閒線程時,則會新建一個線程去執行新提交的任務。若是已經達到核心線程數量, 則會加入到阻塞隊列中。內存
2.maximumPoolSize: 線程池的最大容量。當線程池的阻塞隊列放滿了, 而且線程數量還未達到線程池的最大線程數量, 則會建立新的線程,直到達到最大值
3.keepAliveTime 當阻塞隊列裏面的任務被執行完了, 且有空閒線程時,指定大於核心線程池數量的部分空閒線程的存活時間, 畢竟線程也是須要消耗資源的,及時回收頗有必要。當線程空閒的時間超過這個時間後,會回收掉一部分空閒線程,使其線程池中的線程數量不大於核心線程的數量
4.unit 和keepAliveTIme 配套使用,上面指定了時間的數值,可是沒有指定時間的單位(時,分,秒等), 這裏須要指定時間的單位
5.workQueue 阻塞隊列,當沒有空閒線程時,多餘的任務緩存的地方。
6.threadFactory 線程工廠,用來建立線程時,設定線程的一些參數。一般咱們爲了後續查看日誌方便,能夠經過這個來指定咱們自定義的線程池的線程名稱
7.handler 當線程數量達到最大值時,且阻塞隊列慢了, 後續在提交任務時,沒有地方能夠接受繼續的提交的任務。這種狀況下的一個拒絕策略。
拒絕策略jdK,提供了四種:
// 由提交任務的線程執行任務
public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } }
// 不在接收新的任務,直接拋出異常 public static class AbortPolicy implements RejectedExecutionHandler { public AbortPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } }
// 不接收也不拋出異常,空實現,忽略該任務 public static class DiscardPolicy implements RejectedExecutionHandler { public DiscardPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { } } // 拋棄最老的任務,從阻塞隊列中移除最先提交的任務,而後將該任務加入。 public static class DiscardOldestPolicy implements RejectedExecutionHandler { public DiscardOldestPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } } }
講解完了建立線池時,各參數的做用,那麼咱們如今再反過來看爲何不讓使用jdk提供的apI來建立線程池,而是須要咱們自定義線程池。
newFixedThreadPool ,newSingleThreadExecutor 這兩種api 使用的阻塞隊列都是無界隊列,也就是不管有多少個任務來,咱們都接收。咱們的內存是有限的,阻塞隊列裏面存儲的任務是越多,也就意味着佔用的內存越多,這樣會致使佔用大量的內存,容易引發OOM
newCachedThreadPool 而這個api 的阻塞隊列容量爲0,最大線程數量爲Integer 的最大值。每當有一個任務提交時,阻塞隊列存儲不了,就會新開啓一個線程,當任務比較多,則會建立大量的線程, 引發OOM.
這就是爲何咱們在使用線程池時必定要自定義線程池的緣由了。