做爲 Java 程序員,不管是技術面試、項目研發或者是學習框架源碼,不完全掌握 Java 多線程的知識,作不到心中有數,幹啥都沒底氣,尤爲是技術深究時每每略顯發憷。
在 JDK1.5 之前,研發人員在面對線程頻繁調度的場景,必須手動打造線程池,來節約系統開銷(畫外音:真是吃了很多苦頭)。程序員
從 JDK1.5 開始,Java 提供了一個 Excutors 工廠類來生產線程池,能夠幫助研發人員有效的進行線程控制(畫外音:不用造輪子啦,爽歪歪)。
面試
(配圖釋義:JDK 1.8 能用 Excutors 建立的線程池)json
如上圖示意,Excutors 提供了知足各類場景的線程池建立方式, Java 研發人員就不用苦逼哈哈的去造輪子啦,誰用誰爽。微信
可是,阿里開發規約明確強制研發人員:線程池不容許使用 Executors 去建立,而是經過 ThreadPoolExecutor 的方式。多線程
(配圖釋義:阿里巴巴Java開發手冊,線程池建立規約)架構
不過,若常常關注源碼的同窗會發現,不管是 newFixedThreadPool() 方法、newSingleThreadExecutor() 方法,仍是 newCachedThreadPool() 方法,其背後均使用了 ThreadPoolExecutor。app
(配圖釋義:JDK 1.8 能用 Excutors 建立的線程池的背後)框架
經過上面源碼截圖,能夠清晰看出,以上幾種建立線程池的方式,均是對 ThreadPoolExecutor 類的封裝,因此要想完全掌握線程池,勢必要吃透線程池背後的 ThreadPoolExecutor。編輯器
1函數
解剖:構造函數
有關 ThreadPoolExecute 構造函數,不少書上或者文章都會提到,下面再簡單瞭解一下每一個參數的具體含義。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
構造函數的參數釋義:
corePoolSize:指定線程池中的線程數量;
maximumPoolSize:指定線程池中的最大線程數量;
keepAliveTime:當線程池中線程數量超過 corePoolSize 時,空閒線程的存活時間;
unit:keepAliveTime 的單位;
workQueue:任務隊列,存放提交還沒有被執行的任務;
threadFactory:線程工廠,用於建立線程,通常用默認的便可;
handler:拒絕策略,當任務太多來不及處理,如何拒絕任務。
以上參數除了 workQueue 以及 handler 外,大部分都很易懂。接下來重點說說 workQueue 以及 handler 兩個參數。
參數 BlockingQueue<Runnable> workQueue,是用於存放提交還沒有被執行的任務的隊列,類型是 BlockingQueue 接口的對象,用於存放 Runnable 對象。
參數 RejectedExecutionHandler handler 是指當任務數量超過系統承載能力時,該如何處理?其中 JDK 提供了四種拒絕策略。
(配圖釋義:JDK 1.8 內置的拒絕策略)
JDK 提供的四種拒絕策略概括,簡單瞭解一下。
2
瞭解完 ThreadPoolExecutor 類的構造函數,接下來探討一下阿里開發手冊明確強制的一條使用線程池的規約。
爲了更清晰的認識,不妨走進源碼看一看。首先走進 newFixedThreadPool() 方法的源碼,一探究竟。
如源碼截圖所示,newFixedThreadPool() 方法的實現,返回一個 corePoolSize 和 maximumPoolSize 大小同樣的,而且使用了 LinkedBlockingQueue 任務隊列的線程池。
如上面 LinkedBlockingQueue 的源碼所示,隊列的默認長度爲 Integer.MAX_VALUE,那麼當任務提交頻繁時,線程池中的線程處理不過來時,隊列可能會迅速膨脹,從而會出現 OOM。
接着走進 newSingleThreadExecutor() 方法的源碼,看看有沒有新大陸。
如源碼截圖示意,newSingleThreadExecutor 方法實現中,corePoolSize 和 maximumPoolSize 設置的值均爲 1,返回一個單線程的線程池,而且使用 LinkedBlockingQueue 任務隊列來存在提交的任務,與 newFixedThreadPool() 方法同樣,當任務提交頻繁時,線程池中的線程處理不過來時,隊列會迅速膨脹,從而會出現 OOM。
最後看看 newCachedThreadPool() 方法的源碼實現,一探究竟。
如上圖源碼示意,newCachedThreadPool() 方法實現,返回了一個 corePoolSize 爲 0,maximumPoolSize 的值爲 Integer.MAX_VALUE,而且使用 SynchronousQueue 做爲任務隊列的線程池。
而 SynchronousQueue 隊列是一種直接提交的隊列(不會保存提交的任務),因此總會使線程池增長新的線程來執行任務,當任務執行完畢後,因爲 corePoolSize 爲 0,所以空閒線程又會在 60 秒內被回收。
若是同時有大量任務被提交,而任務的執行又不那麼快時,newCachedThreadPool() 方法,便會開啓大量的線程進行處理,這樣可能很快耗盡系統的資源,進而致使 OOM。
3
寄語寫最後
本次,主要引入線程池背後的 ThreadPoolExecutor 類,算是正式開啓探尋線程池背後的奧祕之旅,先有個初步的認識,知其然知其因此然,後續會逐步深刻。
本文分享自微信公衆號 - 一猿小講(yiyuanxiaojiangV5)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。