如果在線程池中使用無界阻塞隊列會發生什麼問題?要回答這個問題首先要對線程池的工作原理非常熟悉,如果忘了的可以看下這篇文章線程池的核心參數及工作原理。
線程池用無界阻塞隊列會有什麼影響
有一個面試題:在遠程服務異常的情況下,使用無界阻塞隊列是否會導致內存異常飆升?這個問題的意思是遠程服務不可用會導致接口調用超時,新任務不斷提交到線程池,隊列變成得越來越大,此時會導致內存飆升起來,而且還可能會導致內存溢出OOM。通常線程池都是設置成有界隊列加一個拒絕策略。
有界隊列可以避免內存溢出,但是如果線程池的核心參數maximumPoolSize=Integer.MAX_VALUE的話,代表你可以無限制的不停地創建額外的線程出來處理任務,一臺機子有幾千甚至幾萬個線程,我們知道每個線程都有自己的線程棧,是需要佔用一定內存資源的,垃圾回收又回收不了,會導致內存資源被耗盡,系統也會崩潰掉,即使沒有崩潰也會讓機子的cpu負載特別高。所以線程池一般建議maximumPoolSize和corePoolSize設置成一樣,當工作隊列存滿了之後,交給拒絕策略去處理。
線程池自帶的四種拒絕策略
1、ThreadPoolExecutor.AbortPolicy
直接丟棄任務並拋出RejectedExecutionException異常,這是線程池默認的拒絕策略
2、ThreadPoolExecutor.DiscardPolicy
也是直接丟棄任務,但是不拋出異常
3、ThreadPoolExecutor.DiscardOldestPolicy
丟棄隊列老的任務,然後重新嘗試執行任務(重複此過程)
4、ThreadPoolExecutor.CallerRunsPolicy
由調用線程(理解成主線程)處理該任務
線程池隊列滿了,你該怎麼辦
真正的生產環境可以根據自己業務需要,選擇通過實現RejectedExecutionHandler接口來自定義拒絕策略。比如把線程池無法執行的任務信息持久化寫入資料庫去,後臺專門啟動一個線程,後續等待線程池的工作負載降低了,這個後臺線程就可以慢慢的從磁碟裡讀取之前持久化的任務重新提交到線程池。
線上機器宕機,線程池阻塞隊列中的請求怎麼辦
我們知道隊列裡任務請求如果只在內存,沒有持久化的話,機器宕機隊列中的請求肯定會丟失。那麼是不是可以在任務提交到線程池前,先把任務信息插入到資料庫加個狀態欄位:未提交,當任務提交到線程池後,再更新狀態為已提交,任務執行完之後再更新狀態為已完成。機器宕機系統重啟後,啟動一個後臺線程去讀取未提交和已提交的任務信息,重新提交到線程池,繼續執行任務。