目錄數據庫
一 使用線程池的好處編程
池化技術相比你們已經家常便飯了,線程池、數據庫鏈接池、Http 鏈接池等等都是對這個思想的應用。池化技術的思想主要是爲了減小每次獲取資源的消耗,提升對資源的利用率。bash
線程池提供了一種限制和管理資源(包括執行一個任務)。 每一個線程池還維護一些基本統計信息,例如已完成任務的數量。併發
這裏借用《Java 併發編程的藝術》提到的來講一下使用線程池的好處:框架
二 Executor 框架異步
2.1 簡介函數
Executor 框架是 Java5 以後引進的,在 Java 5 以後,經過 Executor 來啓動線程比使用 Thread 的 start 方法更好,除了更易管理,效率更好(用線程池實現,節約開銷)外,還有關鍵的一點:有助於避免 this 逃逸問題。工具
補充:this 逃逸是指在構造函數返回以前其餘線程就持有該對象的引用. 調用還沒有構造徹底的對象的方法可能引起使人疑惑的錯誤。性能
Executor 框架不只包括了線程池的管理,還提供了線程工廠、隊列以及拒絕策略等,Executor 框架讓併發編程變得更加簡單。ui
2.2 Executor 框架結構(主要由三大部分組成)
1) 任務(Runnable /Callable)
執行任務須要實現的 Runnable 接口 或 Callable接口。Runnable 接口或 Callable 接口 實現類均可以被 ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 執行。
2) 任務的執行(Executor)
以下圖所示,包括任務執行機制的核心接口 Executor ,以及繼承自 Executor 接口的 ExecutorService 接口。ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 這兩個關鍵類實現了 ExecutorService 接口。
這裏提了不少底層的類關係,可是,實際上咱們須要更多關注的是 ThreadPoolExecutor 這個類,這個類在咱們實際使用線程池的過程當中,使用頻率仍是很是高的。
注意: 經過查看 ScheduledThreadPoolExecutor 源代碼咱們發現 ScheduledThreadPoolExecutor 其實是繼承了 ThreadPoolExecutor 並實現了 ScheduledExecutorService ,而 ScheduledExecutorService 又實現了 ExecutorService,正如咱們下面給出的類關係圖顯示的同樣。
ThreadPoolExecutor 類描述:
//AbstractExecutorService實現了ExecutorService接口public class ThreadPoolExecutor extends AbstractExecutorService複製代碼
ScheduledThreadPoolExecutor 類描述:
//ScheduledExecutorService實現了ExecutorService接口public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService複製代碼
3) 異步計算的結果(Future)
Future 接口以及 Future 接口的實現類 FutureTask 類均可以表明異步計算的結果。
當咱們把 Runnable接口 或 Callable 接口 的實現類提交給 ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 執行。(調用 submit() 方法時會返回一個 FutureTask對象)
2.3 Executor 框架的使用示意圖
三 (重要)ThreadPoolExecutor 類簡單介紹
線程池實現類 ThreadPoolExecutor 是 Executor 框架最核心的類。
3.1 ThreadPoolExecutor 類分析
ThreadPoolExecutor 類中提供的四個構造方法。咱們來看最長的那個,其他三個都是在這個構造方法的基礎上產生(其餘幾個構造方法說白點都是給定某些默認參數的構造方法好比默認制定拒絕策略是什麼),這裏就不貼代碼講了,比較簡單。
/** * 用給定的初始參數建立一個新的ThreadPoolExecutor。 */ public ThreadPoolExecutor(int corePoolSize,//線程池的核心線程數量 int maximumPoolSize,//線程池的最大線程數 long keepAliveTime,//當線程數大於核心線程數時,多餘的空閒線程存活的最長時間 TimeUnit unit,//時間單位 BlockingQueue<Runnable> workQueue,//任務隊列,用來儲存等待執行任務的隊列 ThreadFactory threadFactory,//線程工廠,用來建立線程,通常默認便可 RejectedExecutionHandler handler//拒絕策略,當提交的任務過多而不能及時處理時,咱們能夠定製策略來處理任務 ) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }複製代碼
下面這些對建立 很是重要,在後面使用線程池的過程當中你必定會用到!因此,務必拿着小本本記清楚。
ThreadPoolExecutor 3 個最重要的參數:
ThreadPoolExecutor其餘常見參數:
下面這張圖能夠加深你對線程池中各個參數的相互關係的理解(圖片來源:《Java性能調優實戰》):
ThreadPoolExecutor 飽和策略定義:
若是當前同時運行的線程數量達到最大線程數量而且隊列也已經被放滿了任時,ThreadPoolTaskExecutor 定義一些策略:
舉個例子:
Spring 經過 ThreadPoolTaskExecutor 或者咱們直接經過 ThreadPoolExecutor 的構造函數建立線程池的時候,當咱們不指定 RejectedExecutionHandler 飽和策略的話來配置線程池的時候默認使用的是 ThreadPoolExecutor.AbortPolicy。在默認狀況下,ThreadPoolExecutor 將拋出 RejectedExecutionException 來拒絕新來的任務 ,這表明你將丟失對這個任務的處理。 對於可伸縮的應用程序,建議使用 ThreadPoolExecutor.CallerRunsPolicy。當最大池被填滿時,此策略爲咱們提供可伸縮隊列。(這個直接查看 ThreadPoolExecutor 的構造函數源碼就能夠看出,比較簡單的緣由,這裏就不貼代碼了。)
3.2 推薦使用 ThreadPoolExecutor 構造函數建立線程池
在《阿里巴巴 Java 開發手冊》「併發處理」這一章節,明確指出線程資源必須經過線程池提供,不容許在應用中自行顯示建立線程。
爲何呢?
使用線程池的好處是減小在建立和銷燬線程上所消耗的時間以及系統資源開銷,解決資源不足的問題。若是不使用線程池,有可能會形成系統建立大量同類線程而致使消耗完內存或者「過分切換」的問題。
另外《阿里巴巴 Java 開發手冊》中強制線程池不容許使用 Executors 去建立,而是經過 ThreadPoolExecutor 構造函數的方式,這樣的處理方式讓寫的同窗更加明確線程池的運行規則,規避資源耗盡的風險
Executors 返回線程池對象的弊端以下:
FixedThreadPool 和 SingleThreadExecutor : 容許請求的隊列長度爲 Integer.MAX_VALUE,可能堆積大量的請求,從而致使 OOM。CachedThreadPool 和 ScheduledThreadPool : 容許建立的線程數量爲 Integer.MAX_VALUE ,可能會建立大量線程,從而致使 OOM。
方式一:經過ThreadPoolExecutor構造函數實現(推薦)
方式二:經過 Executor 框架的工具類 Executors 來實現 咱們能夠建立三種類型的 ThreadPoolExecutor:
對應 Executors 工具類中的方法如圖所示:
四. 幾種常見的線程池詳解
4.1 FixedThreadPool
4.1.1 介紹
FixedThreadPool 被稱爲可重用固定線程數的線程池。經過 Executors 類中的相關源代碼來看一下相關實現:
/** * 建立一個可重用固定數量線程的線程池 */ public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory); }複製代碼
另外還有一個 FixedThreadPool 的實現方法,和上面的相似,因此這裏很少作闡述:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }複製代碼
從上面源代碼能夠看出新建立的 FixedThreadPool 的 corePoolSize 和 maximumPoolSize 都被設置爲 nThreads,這個 nThreads 參數是咱們使用的時候本身傳遞的。
4.1.2 執行任務過程介紹
FixedThreadPool 的 execute() 方法運行示意圖:
上圖說明:
4.1.3 爲何不推薦使用FixedThreadPool?
FixedThreadPool 使用無界隊列 LinkedBlockingQueue(隊列的容量爲 Intger.MAX_VALUE)做爲線程池的工做隊列會對線程池帶來以下影響 :
4.2 SingleThreadExecutor 詳解
4.2.1 介紹
SingleThreadExecutor 是隻有一個線程的線程池。下面看看SingleThreadExecutor 的實現:
/** *返回只有一個線程的線程池 */ public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory)); }複製代碼
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }複製代碼
從上面源代碼能夠看出新建立的 SingleThreadExecutor 的 corePoolSize 和 maximumPoolSize 都被設置爲 1.其餘參數和 FixedThreadPool 相同。
4.2.2 執行任務過程介紹
SingleThreadExecutor 的運行示意圖:
上圖說明;
4.2.3 爲何不推薦使用FixedThreadPool?
SingleThreadExecutor 使用無界隊列 LinkedBlockingQueue 做爲線程池的工做隊列(隊列的容量爲 Intger.MAX_VALUE)。SingleThreadExecutor 使用無界隊列做爲線程池的工做隊列會對線程池帶來的影響與 FixedThreadPool 相同。說簡單點就是可能會致使 OOM,
4.3 CachedThreadPool 詳解
4.3.1 介紹
CachedThreadPool 是一個會根據須要建立新線程的線程池。下面經過源碼來看看 CachedThreadPool 的實現:
/** * 建立一個線程池,根據須要建立新線程,但會在先前構建的線程可用時重用它。 */ public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory); }複製代碼
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }複製代碼
CachedThreadPool 的 corePoolSize 被設置爲空(0),maximumPoolSize 被設置爲 Integer.MAX.VALUE,即它是無界的,這也就意味着若是主線程提交任務的速度高於 maximumPool 中線程處理任務的速度時,CachedThreadPool 會不斷建立新的線程。極端狀況下,這樣會致使耗盡 cpu 和內存資源。
4.3.2 執行任務過程介紹
CachedThreadPool 的 execute()方法的執行示意圖:
上圖說明:
4.3.3 爲何不推薦使用CachedThreadPool?
CachedThreadPool容許建立的線程數量爲 Integer.MAX_VALUE ,可能會建立大量線程,從而致使 OOM。
記得關注(下篇)已經給你們準備好了
歡迎你們點贊關注轉發一塊兒來討論。會天天給你們帶來一到兩個知識點,一塊兒成長。