從Runnable中的執行時異常說起

2021-09-01 10:07:41 字數 2440 閱讀 6285

前段時間,夜晚突然收到報警,緊急上線排查。由於dba操作不當,大片資料回滾,發生鎖表的情況,請求返回時間過長,使得系統列印出大量的rejectedexecutionexception的異常。定位到**片段類似:

threadpoolexecutor workers = new threadpoolexecutor(10, 600, 30, timeunit.seconds, new arrayblockingqueue(10));

sharetask sharetask = new sharetask(shareinfo, clientid, rtnresult,

outersharecontext, importouterfriend, bindingmanager,shareusermanager);

workers.execute(sharetask);

這裡就要說說threadpoolexecutor和arrayblockingqueue了,眾所周知arrayblockingqueue類是乙個阻塞的佇列。當和threadpoolexecutor使用時,threadpoolexecutor會在初始化時開啟corepoolsize(也就是上面**中的10)個執行緒,去消費佇列裡的task。當併發量增大,直到corepoolsize全都在執行task,threadpoolexecutor不會立即新建消費執行緒,而是將新加入的task放入到對佇列中,一直到放滿,threadpoolexecutor才會去建立乙個新的執行緒。直至達到執行緒池中的消費執行緒達到maximumpoolsize(也就是上面**裡的600)。如果這個時候再有task加入,根據預設的飽和策略,將會丟擲rejectedexecutionexception異常。

這次就是由於獲取資料庫等待時間超長,導致task響應時間變慢,繼而執行緒池中活躍的消費執行緒堆積到600個執行緒依然無法應付,才丟擲的rejectedexecutionexception。

顯然丟擲rejectedexecutionexception不是那麼的友好,我們在這裡可以自定義飽和策略。預設系統飽和策略是丟擲異常。

workers.setrejectedexecutionhandler(new rejectedexecutionhandler()

});

另外當執行緒故障恢復時,通過日誌驚奇的觀察到,佇列中的執行緒以序列的方式執行了一短時間,不過很快就正常了。於是在本地寫了乙個測試。

threadpoolexecutor workers = new threadpoolexecutor(10, 100, 3, timeunit.seconds, new arrayblockingqueue(10));

for (int i = 0; i < 110; i++) catch (interruptedexception e)

throw new runtimeexception();

}});

}while (true) catch (interruptedexception e)

system.out.println(string.format("task count:%s; active count:%s", workers.gettaskcount(), workers.getactivecount()));

}

執行如上的**,列印日誌如下:

task count:110; active count:100

task count:10; active count:1

task count:10; active count:1

task count:9; active count:1

task count:9; active count:1

...task count:2; active count:1

task count:2; active count:1

task count:1; active count:1

task count:1; active count:1

可以看到,最後的10個task是以類似序列的方式在執行。

這裡的原因在於threadpoolexecutor的中斷策略,當runnable中丟擲rte時,threadpoolexecutor會將執行當前的runnable的執行緒dead。由於例子的**100%會丟擲rte,最終的結果就是threadpoolexecutor中存活的消費執行緒數變為0。threadpoolexecutor建立執行緒只有在初始化,呼叫excute等幾個主動方法中才會去做,我們任務的提交早在一開始就已經做了,最終導致的結果就是永遠只存在乙個執行緒服務這個任務佇列。

那麼要比較優雅的解決這個問題,可以使用futuretask,futuretask裡面會處理執行時異常,不會將其丟擲給threadpoolexecutor。

java執行時異常和非執行時異常的區別

建議使用執行時異常和編譯時異常叫法,便於區分和理解 非執行時異常是什麼異常?很懵逼 編譯時異常 程式沒有通過編譯器的編譯,必須處理掉這個異常程式才能正常執行,比如檔案路徑找不到異常,類找不到異常,io異常,必須用try catch或是throwable處理掉才能編譯通過 可以理解為一種特殊的語法錯誤...

php程式執行時間 php計算程式的執行時間

首先我們分析一下原理,要想得到程式執行時間,那麼可以在程式最開始執行的時候定義乙個變數記下當前時間,然後等我們程式執行完之後再記錄一下當前的時間,兩者相差就是該程式執行花費的時間了。這裡介紹一下 microtime 這個函式,microtime 用的不多,但是不能不知道這個函式,它是返回當前 uni...

ios中執行時學習筆記

1.什麼是執行時?1 執行時是一套純c語言的api 純c語言庫 2 編譯器最終都會將oc 轉化 為執行時 clang rewrite objc m 3 利用執行時,可以做很多底層的操作,比如 動態新增物件的成員變數和成員方法 動態交換兩個方法的實現 特別是交換系統自帶的方法 獲得某個類的所有成員方法...