中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么解決異步任務所導致的問題

發布時間:2021-10-20 09:14:30 來源:億速云 閱讀:499 作者:iii 欄目:web開發

這篇文章主要介紹“怎么解決異步任務所導致的問題”,在日常操作中,相信很多人在怎么解決異步任務所導致的問題問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么解決異步任務所導致的問題”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

前言

先簡單說下這段代碼,就是使用一個異步線程執行一段業務邏輯,示例代碼如下:

// 前置邏輯 ..... Thread thread=new Thread(new Runnable() {     @Override     public void run() {         try {            // 異步線程執行其他業務邏輯         } catch (Exception e) {            // 不進行任何代碼處理         }     } }); thread.start();

憑著老程序員的經驗,猜到可能是異步線程內發生了異常,導致異步線程退出,不再繼續執行。而又因為上述代碼「吃掉」了異常,這就導致我們從外部看起來這個工程跑著跑著就不動了,日志什么也沒了。

于是改造了一下,打印出相關異常日志,最終定位問題,原來是小姐姐造的數據存在問題,從而引發 NPE 問題。

「不知道大家有沒有碰到過上面的情況,使用線程異步執行相關邏輯,但是執行到一半突然就像卡主一般,不再繼續往下執行。」

總結起來分為下面三種情況:

  • 異步任務長時間被阻塞

  • 異步任務發生異常

  • 異步任務異常被吃掉

異步任務長時間被阻塞

第一種,異步線程執行任務,這個任務需要通過網絡調用其他遠端服務。假設服務端響應的非常慢,而我們設置的網絡超時時間又很長,這就會導致這個線程長時間被阻塞。

假設異步任務偽碼如下:

ThreadPoolExecutor threadPool= ....; threadPool.execute(() -> { // 1.調用遠端服務 Socket socket....; // 2.設置超時時間 socket.setSoTimeout(60*1000); // 3.讀取服務端返回 socket.read(); });

上面程序中,如果服務端一直沒有返回,那么異步線程將會一直被阻塞,直到超時。

這種情況其實還好,我們無非等待一段時間,就可以看到異步線程繼續往下執行任務。

舉一個極端的例子,假設上面的代碼沒有設置超時時間,而服務端一直沒有返回響應,「此時異步線程就會被一直阻塞」。

除了上面網絡讀取阻塞的例子,常見情況還有

  • 執行了長時間休眠,比如 TimeUnit.MINUTES.sleep(60)

  • 內部發生了死鎖

  • 等等

如果異步線程長時間被阻塞,而異步任務執行又比較頻繁,那么線程池內可用線程將會被慢慢耗盡,此時后續任務就會被拒絕執行。

解決辦法

其實非常簡單,首先我們使用 jstack 命令 「dump」 一下當前 Java 應用的線程堆棧情況,然后根據線程池名字定位相關線程即可。

怎么解決異步任務所導致的問題

網上隨便找了堆棧圖

如果沒有自定義線程池 ThreadFactory 參數,那查找定位被阻塞線程就比較麻煩了。

所以創建線程池建議自定義 ThreadFactory 參數,這對于后期排查問題非常有用。

異步任務異常未捕獲

上面的情況,異步線程其實還活著,只是被阻塞沒辦法執行后續的邏輯。

那這一類情況呢,與上面不太一樣,由于異步任務內部發生錯誤,拋出異常,而代碼邏輯中又沒有進行捕獲處理,從而導致線程提前異常退出。

異常退出偽碼如下:

// 1.創建執行的任務 Runnable runnable=new Runnable() {     @Override     public void run() {        // 執行前置邏輯         // 拋出異常         int i=100/0;        // 執行后置邏輯              } }; // 2.創建線程 Thread thread=new Thread(runnable); // 3.運行異步線程 thread.start(); // 其他業務邏輯

上述代碼中,異步線程執行到除零邏輯,將會拋出異常,然后異步線程將會異常退出。

「異步線程內拋出的異常日志僅僅只會被打印到控制臺,而不會被記錄到日志文件中。」

所以正常的業務日志中是見不到線程異常的日志,這就給了我們一種假象,異步線程看起來還在執行任務,其實它已經掛了。

PS:上面的話可能不好理解,舉個例子,如果你使用 IDEA 執行上面這段程序,異常日志將會被輸出到 IDEA 下方控制臺。

而如果我們在 Linux 機器上執行這段程序,異常日志僅僅只會顯示在當前終端窗口上,一旦關閉當前終端窗口,日志就沒。了。

如果想要保存這種日志,我們需要將 stdout 重定向到日志文件中,比如執行以下命令:

-- 將 stdout 重定向輸出到文件中 nohup java  xxxx > $STDOUT_FILE 2>&1 &

解決辦法

第一種解決辦法,其實很多讀者已經想到了,異步線程內使用 try..catch 語句捕獲所有異常即可。

「沒錯,就是這么簡單。」

不過這里提一點,一般我們使用 try..catch僅僅只會捕獲 Exception異常。

那么極端情況下,異步線程內如果拋出 Error,比如拋出了  java.lang.NoClassDefFoundError,此時是沒法捕獲,異步線程依舊會異常退出。

所以我們可以使用try..catch捕獲 Throwable,這樣及時發生 Error錯誤,也會被捕獲。

不過個人覺得捕獲Exception異常就夠了,正常工程應用很少會發生 Error錯誤,所以我們只要了解有這個可能即可。

ps:之前同事上線一個應用,使用異步線程執行任務,每次執行到一半,都不再繼續執行。

由于異步線程內使用try..catch捕獲處理了 Exception異常,所以找了半天不知道什么問題。

最后,小黑哥排查 stdout 輸出日志,才發現異步線程發生 Error錯誤。

這種解決本法需要我們主動去捕獲異常,而下面第二種解決辦法,設置線程異常處理方法。

一旦設置完成,如果異步線程內發生異常,線程退出之前將會調用異常處理方法。

我們拿 Thread 來講,其設置方法如下:

Runnable runnable=new Runnable() {     @Override     public void run() {         int i=100/0;     } };  Thread thread=new Thread(runnable); thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {     @Override     public void uncaughtException(Thread t, Throwable e) {         System.out.println(t.getName()+"發生異常"+e.getMessage());     } }); thread.start();

不過生產環境不建議直接使用 Thread,我們需要使用線程池代替。

線程池設置異常處理方法可以分為兩種,如果我們使用 ThreadPoolExecutor#execute執行異步任務,那我們需要在自定義線程池的時候,使用  ThreadFactory 設置。

ThreadPoolExecutor threadPool =new ThreadPoolExecutor(         5,         10,         60,         TimeUnit.SECONDS,new ArrayBlockingQueue<>(100),       // 這里使用 Guava 的 ThreadFactoryBuilder 類,方便構造 ThreadFactory         new ThreadFactoryBuilder().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {             @Override             public void uncaughtException(Thread t, Throwable e) {                 // 處理異常             }         }).build()         );

如果你當前使用 ThreadPoolExecutor#submit執行異步任務,那就簡單了,我們可以直接通過  Future#get獲取到線程內拋出的異常。

Future<?> future = threadPool.submit(new Callable<Object>() {     @Override     public Object call() throws Exception {         return "小黑十一點半";     } });  try {     future.get(); } catch (InterruptedException e) {     e.printStackTrace(); } catch (ExecutionException e) {     // 線程內拋出異常將會被封裝在 ExecutionException 內 }

異步任務異常被吃掉

好了,終于到最后一種情況了,小黑哥這次碰到就是這種??。

這種情況具體來說就是異步線程內使用  try..catch 語句捕獲了所有異常,但是沒有在 catch語句中進行任何代碼處理。

Thread thread=new Thread(new Runnable() {     @Override     public void run() {         try {             int i=100/0;         } catch (Exception e) {            // 不進行任何代碼處理         }     } }); thread.start();

如上述代碼所示,catch語句中沒有進行任何代碼處理。即使異步線程內真發生了異常,也不會有任何提示,這個異常就像被吃掉一般。

到此,關于“怎么解決異步任務所導致的問題”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

平顶山市| 武隆县| 高邑县| 巴马| 和硕县| 湖州市| 江永县| 吴桥县| 宁城县| 墨脱县| 黄山市| 额敏县| 峡江县| 安图县| 海原县| 桂东县| 梨树县| 岫岩| 江源县| 阳谷县| 佛山市| 凯里市| 安泽县| 双流县| 宁化县| 醴陵市| 东源县| 托克逊县| 阿坝县| 灵山县| 时尚| 三穗县| 梅河口市| 灌阳县| 揭东县| 长乐市| 尤溪县| 炎陵县| 滨州市| 冷水江市| 邵阳市|