自定義JAVA線程池拒絕策略

最近一直被隊列的消費業務所困擾,先大致說下業務狀況。

模塊A產生數據通過隊列傳遞給模塊B處理,但是數據來自於定時任務,經常是瞬時上萬條或者更多,而且模塊B的消費有限速控制並且能力有限(消費業務使用的線程池),肯定需要時間消化。

那麼帶來的一個問題就是線程池的拒絕策略選哪種?

首先說下線程池的四種拒絕策略:

  1. AbortPolicy:直接拋出異常。
  2. CallerRunsPolicy:只用調用者所在線程來運行任務。
  3. DiscardOldestPolicy:丟棄隊列裏最老的一個任務,並執行當前任務。
  4. DiscardPolicy:不處理,丟棄掉。

最初直觀感受就是不能丟消息,用2吧,主線程跟着做業務。

久了,發現一個問題,主線程(暫且這麼說吧,準確說是啓動線程池的線程)一旦運行任務,即使線程池裏的線程跑完任務都不會再進任務,餓着呢。直到主線程跑完一次業務,才能繼續消費,分配給線程池任務。

問題很明顯,業務流程比較耗時,主線程被佔住了,線程池的一旦幹完活,啥都幹不了,都等着主線程消費隊列的數據給新任務呢。

忍不了,但是分析其他三個策略,AbortPolicy直接拋異常,拋了能咋樣,還是不知道要幹啥;DiscardOldestPolicy丟棄老任務,丟消息,否了;DiscardPolicy丟棄,肯定否了。

於是查看拒絕策略源碼,發現拒絕策略的這幾個類真是夠簡潔了(只有兩個方法),統一實現RejectedExecutionHandler接口,實現rejectedExecution方法(代碼幾行……),還有自己的構造方法(空的)。

然後分析自己的業務需求,總結:線程池的阻塞隊列滿了後,主線程啥都不幹,就等着阻塞隊列不滿的時候,把任務扔給線程池。

看源碼發現阻塞隊列正好有個remainingCapacity接口,看看字面意思就知道是啥意思了,不過本着咱們猿們嚴謹的態度,繼續深入看接口源碼(這裏不帶着看了,確認是隊列空餘個數)。然後主線程只要不斷獲取空餘個數,是0就繼續獲取,直到不是0爲止。代碼如下:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
     if (!e.isShutdown()) {
          while (e.getQueue().remainingCapacity() == 0);
          e.execute(r);
     }
 }

Runnable r :主線程

ThreadPoolExecutor e:線程池

建議先看看其他四種策略的實現。

原以爲這個問題比較複雜,結果只用幾行代碼搞定,不知道有沒有坑……

ps:如果有問題,請指正交流。如果有更好的方案,歡迎指導。

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