摘要:這是在具體代碼中發現的不當延遲的問題,極端情況下可能把內存打爆。
本文分享自華為雲社區《線程中不當使用延遲問題》,原文作者:技術火炬手 。
背景
這是在具體代碼中發現的不當延遲的問題,極端情況下可能把內存打爆。
代碼DevLicenseServiceRoaDelegateImpl.java
定義:
使用:
signalRefreshHelp 定義
這段代碼最大問題是使用延遲算法不當,在極端情況下會導致內存暴漲,嚴重影響服務程序的性能。
不要使用sleep來實現延遲
使用sleep實現延遲看起來非常直觀,但是這個在高並發、多請求、長期運行的服務程序里必須特別小心。這是因為衡量服務程序性能的一個非常重要的指標是QPS, 就是服務程序的處理能力,一般情況下越大越好;服務程序的總並發能力等於每個線程的qps;單個線程的QPS = 1000毫秒 / (處理一個請求的毫秒);所以上面那個線程的QPS <= 1000 / 10000 = 0.1 (因為線程sleep了10000毫秒)。
這里的處理邏輯是錯誤的!也有很嚴重的性能隱患,不過幸好調用這個api 請求不多,才沒有導致嚴重問題。
開發者的意圖是在創建一個任務后,延遲10s執行該任務,處理時序圖如下
假如時間點t1 & t2 挨得很接近的話,線程在執行job1 & job2 也是很接近。
但實際的情況變成:
就算創建job1 & job2的時間很接近,但job2執行的時間會比預期多了10s;連續提交的任務越多,越容易堆積,這些堆積的任務存放在 blocking queue,一直到處理完畢才刪除;如果這類請求很多的話,很容易引起內存爆掉。
解決方案
選擇合適的數據結構,默認線程池關聯的隊列是LinkedBlockingQueue , 沒有延遲控制,可以使用DelayQueue
DelayQueue內部使用了PriorityQueue 按時間排序;需要自己使用Delayed 接口封裝請求數據
下面是例子
測試代碼,同時加入 3個需要延遲10s的任務
測試結果:
符合預期