可擴展的Java線程池執行器

分享一下最近優銳課學習筆記。緩存

Java線程池執行程序偏向於排隊而不是產生新線程。從好的方面來講,咱們有兩種解決方法。多線程

理想狀況下,對任何線程池執行程序而言,指望以下:學習

  • 預先建立了一組初始線程(核心線程池大小)來處理負載。
  • 若是負載增長,則應建立更多線程來處理最大線程數(最大池大小)的負載。
  • 若是線程數增長到「最大池大小」之外,則將任務排隊。
  • 若是使用了有界隊列,而且隊列已滿,請引入一些拒絕策略。

下圖描述了該過程;僅建立初始線程來處理任務(負載很低時)。編碼

 

 

 

 隨着更多的任務進入,假設建立的線程總數小於最大池大小,則會建立更多的線程來處理負載(任務隊列仍然爲空)。線程

 

 

 

 若是任務總數大於線程總數(初始+擴展),則任務隊列開始填充:blog

 

 

 不幸的是,Java線程池執行器(TPE)偏向於排隊而不是生成新線程,即,在初始核心線程被佔用以後,任務被添加到隊列中,而且在隊列達到其限制以後(這隻會在有界隊列中發生) ),則會產生額外的線程。若是隊列不受限制,則徹底不會產生擴展線程,以下圖所示。隊列

 

 

 

 

  1. 建立了初始核心線程來處理負載。
  2. 一旦任務數量超過核心線程數,隊列就會開始填滿以存儲任務。
  3. 隊列填滿後,將建立擴展線程。

這是TPE中的代碼,出現了問題io

 

 

 

 

咱們有兩種解決方法:擴展

解決方法1:調整池大小

corePoolSize maximumPoolSize設置爲相同的值,並將allowCoreThreadTimeOut 設置爲true。線程池

優勢

  • 無需編碼技巧。

缺點

  • 因爲線程的建立和終止很是頻繁,所以沒有真正的線程緩存。
  • 沒有適當的可伸縮性。

解決方法2:覆蓋要約方法

  • 重寫委託人TransferQueue的offer方法,並嘗試將任務提供給空閒的工做線程之一。若是沒有等待線程,則返回false。
  • 實現自定義RejectedExecutionHandler以始終添加到隊列中。

 

 

 實現自定義RejectedExecutionHandler以始終添加到位數中。

優勢

  • TransferQueue確保不須要建立線程,並將工做直接轉移到等待隊列。

缺點

  • 不能使用定製的拒絕處理程序,由於它用於插入要排隊的任務。

解決方法#3:使用自定義隊列

使用自定義隊列(TransferQueue)並覆蓋offer方法以執行如下操做:

  1. 嘗試將任務直接轉移到等待隊列(若是有)。
  2. 若是以上操做失敗,而且未達到最大池大小,則經過從offer方法返回false來建立擴展線程。
  3. 不然,將其插入隊列。

 

 

 

 

優勢

  • TransferQueue確保不須要建立線程,並將工做直接轉移到等待隊列。
  • 能夠使用自定義拒絕處理程序。

缺點

  • 隊列和執行程序之間存在循環依賴關係。

解決方法4:使用自定義線程池執行程序

使用專門用於此目的的自定義線程池執行程序。它使用系統@ Facebook規模中所述的LIFO調度。

相關文章
相關標籤/搜索