ThreadPoolExecutor入門級

是什麼

JUC包下提供的一個線程池工具類。它幫我們解決了2個主要問題:

  1. 當需要執行大量異步任務的時候合理、有效的複用線程資源,防止多個任務創建多個線程資源。
  2. 能夠有效限制和管理線程資源,比如控制初始化線程的數量,動態新增線程數等。

實現原理

本篇我們主要圍繞它的核心設計思想講解,不追蹤源碼。JUC包下提供了Excutors工廠類,讓我們快速便捷的實例化ThreadPoolExecutor,但更建議用戶通過手動實例化ThreadPoolExecutor來使用線程池。ThreadPoolExecutor構造器提供了7個初始化參數:

// 線程池的核心線程數
int corePoolSize
//線程池最大線程數
int maximumPoolSize
//超出核心線程外的線程處於空閒時能存活的時間,超過存活時間會自動被回收
long keepAliveTime
//keepAliveTime參數的時間單位
TimeUnit unit
//任務隊列
BlockingQueue<Runnable> workQueue
// 創建線程的工廠
ThreadFactory threadFactory
//拒絕策略
RejectedExecutionHandler handler

現在我們帶着以下2個問題逐步去理解這幾個核心參數的意思以及整一個線程池的設計思想。

  1. ThreadPoolExecutor 它是如何實現線程的複用呢?
  2. 線程池的最大線程數、任務隊列、拒絕策略何時會用到呢?

當用戶提交任務至線程池內部會遵循這樣的一個工作模型,如下圖1-1:
圖1-1
從圖中可以看出它是一個典型的生產者、消費者模型。生產者即用戶添加任務到線程池中,消費者即線程工作集執行添加進來的任務。當用戶第一次提交任務,ThreadPoolExecutor發現當前線程數量小於 核心線程數會立即創建線程進行任務執行,直到超過核心線程數後會將提交的任務放入隊列中,當核心線程執行完任務,會在從任務隊列中取任務執行。任務隊列中滿時,仍然還有任務提交會判斷當前線程數是否大於指定的最大線程數,如果小於則會再次創建線程來執行後面提交的任務,否則會觸發任務拒絕策略。超出核心線程數外創建出來的線程在沒有新任務繼續提交的時候會存活keepAliveTime時間。
爲了便於理解,舉個例子假定我們對線程池設置如下參數

corePoolSize:3 
maximumPoolSize:8
keepAliveTime:1,
unit:分鐘 
workQueue:數組阻塞隊列,容量3
handler:拋出異常策略

那麼此時線程池的工作模型如圖1-1,下面我們分幾種情況進行討論並給出它對應的工作模型圖

  1. 同一時刻用戶提交了4個任務,其中3個任務分配給3個線程進行執行,其中一個任務被放入任務隊列。此時線程池工作模型如圖1-2:
    圖1-2

  2. 同一時刻用戶提交7個任務,其中3個任務分配給3個核心線程執行,3個任務放入任務隊列,最後一個任務分配給 超出核心線程外的線程執行。線程池工作模型如圖1-3:
    圖1-3

  3. 同一時刻用戶提交12任務,其中3個任務分配給3個線程執行,3個任務放入任務隊列,5個任務分配給超出核心線程外的線程執行 ,最後一個任務被拒絕。線程池工作模型如圖1-4
    圖1-4

到這裏ThreadPoolExecutor的基本原理已講解完畢。但在我們使用線程池的時候需要注意兩個點。第一,創建線程池時需要指定線程池名稱,以便於後期bug排查。第二,當程序運行結束時,記得調用shutdown關閉線程池。

應用場景

對於接口中如有日誌記錄(寫入數據到DB),可能需要耗時時間較長時,我們可以採用線程池對日誌記錄採用異步進行處理。

書寫技術文章是一個循序漸進的過程,所以我不能保證每句話、每行代碼都是對的,但至少能保證不復制、不粘貼,每篇文章都是自己對技術的認識、細心斟酌總結出來的。喬布斯說:我們在這個星球上的時間都很短,很少有機會去做幾件真正偉大的事情,同時要做得好,我必須要趁我還年輕的時候完成這些事。
其實我想說的是,我是一枚程序員,我只想在有限的時間內儘可能去沉澱我這一生中所能沉澱下來的東西


清山綠水始於塵,博學多識貴於勤。

我有酒,你有故事嗎?

微信公衆號:「Java錦囊」。

歡迎一起談天說地,聊Java。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章