Executors功能如此強大,ThreadPoolExecutor功不可沒(一)

做爲 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, BlockingQueue<Runnable> 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

 思考:使用 Executors 會致使 OOM?   

瞭解完 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 類,算是正式開啓探尋線程池背後的奧祕之旅,有個初步的認識,知其然知其因此然,後續會逐步深刻。

好了,本次就談到這裏, 一塊兒聊技術、談業務、噴架構,少走彎路,不踩大坑。會持續輸出原創精彩分享,敬請期待!

推薦閱讀:
完全搞懂 Java 線程池,幹啥都再也不發憷
Java程序跑的快,全要靠線程帶
fastjson的這些坑,你誤入了沒?
真實|技術人員該如何站好最後一班崗?
Java 8 的這些特性,你知道嗎?
改掉這些壞習慣,還怕寫不出健壯的代碼?(一)
改掉這些壞習慣,還怕寫不出優雅的代碼? (二)
改掉這些壞習慣,還怕寫不出優雅的代碼? (三)
改掉這些壞習慣,還怕寫不出健壯的代碼? (四)
改掉這些壞習慣,還怕寫不出精簡的代碼? (五)
改掉這些壞習慣,還怕寫不出精簡的代碼? (六)
堅持是一種信仰,在看是一種態度!

本文分享自微信公衆號 - 一猿小講(yiyuanxiaojiangV5)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索