記錄一次生產環境的多執行緒併發問題

2021-10-04 14:20:33 字數 1714 閱讀 6285

1.當時的場景

開乙個執行緒,定時check hbase,避免hbase異常時,阻塞大量請求。

2.程式偽**

偽**如下:

定時用的是jdk自帶的工具類timer

具體邏輯:

private executorservice executorservice = executors.

newsinglethreadexecutor()

;public

void

run(

)catch

(exception e)}}

// 增強for迴圈 遍歷 checklist,得到最終的check結果快取

setcheckresult

(checklist)

;}

到這,不知道你們能不能看出來併發問題?

3.導致的問題

將程式上線到prd的問題:timer停止執行,hbase某個regionserver恢復正常後,由於check停止了,沒有恢復regionserver的狀態,導致了部分請求到該regionserver的請求異常。查日誌,併發問題出在checklist上

4.併發問題

checkthread是由執行緒池中的執行緒執行,在執行異常時雖然呼叫了future.cancel(true);但是這個方法只是給打上了中斷標記,並不能保證一定能中斷執行緒執行。也就是說當此執行緒在往checklist增加內容的同時;

setcheckresult(checklist)是由主線程執行,這個方法正在遍歷checklist:

for

(checkresult result : checklist)

增強for迴圈是採用的迭代器遍歷,也就是說在遍歷過程中如果list中的元素個數有變化就會報錯(fast-fail)。

乙個執行緒在新增內容,乙個執行緒在用增強for迴圈遍歷,最終也就導致了併發問題。

5.解決方案

知道原因,也就很好找解決方案了。

以上主要有兩個問題,乙個是timer停止問題,乙個是併發問題,當然是因為併發問題導致的timer停止

其實在我們這個場景下,只要timer不停止,就算出現併發問題,影響也不大,因為check定時10秒就會執行一次,後面check結果就正常了,畢竟併發問題出現的概率不高。

解決辦法:

1.將run()方法的所有方法體給try catch,不要往外拋異常(timer執行過程中有異常就會停止執行)

2.最簡單的方式,也是我採用的方式:重新new乙個list物件:

//這個方法最終採用的是native方法:system.arraycopy();

list

list =

newarraylist

(checklist)

;

到這就完了,這也更加警示了自己,只要涉及到多執行緒,就一定要小心謹慎,多多思考。因為這種問題一般測試時測不出來的。

一次生產事故的優化經歷

跟蹤web伺服器業務日誌,發現在資料庫更新層報請求不到新的資料庫連線或者資料庫連線已經用完,認為是資料庫的最大連線數太小,於是調整mysql資料庫最大連線數為以往的3倍 下次搶標的時候繼續觀察業務日誌,發現已經不報資料庫鏈結的相關錯誤了,但還是很多使用者反饋搶標時候打不開頁面。在搶標過程中繼續觀察,...

一次生產事故的優化經歷

原 一次生產事故的優化經歷 跟蹤web伺服器業務日誌,發現在資料庫更新層報請求不到新的資料庫連線或者資料庫連線已經用完,認為是資料庫的最大連線數太小,於是調整mysql資料庫最大連線數為以往的3倍 下次搶標的時候繼續觀察業務日誌,發現已經不報資料庫鏈結的相關錯誤了,但還是很多使用者反饋搶標時候打不開...

一次生產事故的優化經歷

分析過程 跟蹤web伺服器業務日誌,發現在資料庫更新層報請求不到新的資料庫連線或者資料庫連線已經用完,認為是資料庫的最大連線數太小,於是調整mysql資料庫最大連線數為以往的3倍 下次搶標的時候繼續觀察業務日誌,發現已經不報資料庫鏈結的相關錯誤了,但還是很多使用者反饋搶標時候打不開頁面。在搶標過程中...