1. 程式人生 > >java多執行緒 ThreadPoolExecutor 策略及坑

java多執行緒 ThreadPoolExecutor 策略及坑

無論是使用jdk的執行緒池ThreadPoolExecutor 還是spring的執行緒池ThreadPoolTaskExecutor 都會使用到一個阻塞佇列來進行儲存執行緒任務。

   當執行緒不夠用時,則將後續的任務暫存到 阻塞佇列中,等待有空閒執行緒來進行。

  當這個阻塞佇列滿了的時候,會出現兩種情況

   正在執行的執行緒數量小於 maximumPoolSize,那麼還是要建立執行緒執行這個任務;

   正在執行的執行緒數量大於或等於 maximumPoolSize,那麼執行緒池會通過一個策略進行對後續的任務進行處理。

四種策略

ThreadPoolExecutor.AbortPolicy()  丟擲java.util.concurrent.RejectedExecutionException異常  ThreadPoolExecutor.CallerRunsPolicy() 重試添加當前的任務,他會自動重複呼叫execute()方法  ThreadPoolExecutor.DiscardOldestPolicy() 拋棄舊的任務  ThreadPoolExecutor.DiscardPolicy() 拋棄當前的任務 

其他很容易看出來,最近踩了一個ThreadPoolExecutor.CallerRunsPolicy()的坑。

情景是這樣的:我需要通過執行緒進行建立長連線,當開啟了15個執行緒的是,執行緒池最大即達到了maximumPoolSize的數量的時候,繼續增大請求量,會無緣無故的多出來

一個長連線,由於有負載均衡,導致了很多請求無法從長連線中得到。

經過很長時間的測試,最終發現了是ThreadPoolExecutor.CallerRunsPolicy()策略導致的。

這個測試是當你的執行緒數達到最大,阻塞佇列也滿了的時候,之後的任務會強制先執行,但是沒有了執行緒誰來執行呢,這個策略會強制中斷主執行緒進行執行這個任務。

即是說,當我的量上來,執行緒池不夠用的時候,中斷了我的主執行緒,主執行緒沒有長連線,就建立了一個新的長連線,那邊進行了負載均衡,但是這邊不會在進行主執行緒了,

導致很多請求接收不到。

假設佇列大小為 10,corePoolSize 為 3,maximumPoolSize 為 6,那麼當加入 20 個任務時,執行的順序就是這樣的:首先執行任務 1、2、3,然後任務 4~13 被放入佇列。這時候佇列滿了,任務 14、15、16 會被馬上執行,最終順序是:1、2、3、14、15、16、4、5、6、7、8、9、10、11、12、13。

踩坑完畢

最終做法:

換成ThreadPoolExecutor.DiscardPolicy()策略,還是從已經建立的連線中進行 的到請求,不要繼續建立連線。