成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

有些線程跑著跑著就不見(jiàn)了

開(kāi)發(fā) 前端
憑著老程序員的經(jīng)驗(yàn),猜到可能是異步線程內(nèi)發(fā)生了異常,導(dǎo)致異步線程退出,不再繼續(xù)執(zhí)行。而又因?yàn)樯鲜龃a「吃掉」了異常,這就導(dǎo)致我們從外部看起來(lái)這個(gè)工程跑著跑著就不動(dòng)了,日志什么也沒(méi)了。

[[377581]]

本文轉(zhuǎn)載自微信公眾號(hào)「小黑十一點(diǎn)半」,作者樓下小黑哥。轉(zhuǎn)載本文請(qǐng)聯(lián)系小黑十一點(diǎn)半公眾號(hào)。   

前言

Hello,大家好,我是樓下小黑哥~

最近接了一個(gè)業(yè)務(wù)需求,需求倒是不難,三下五除二就整理出設(shè)計(jì)方案,然后就開(kāi)始代碼改造。

啪,很快,就完成代碼改造,然后提測(cè)給測(cè)試小姐姐。

小姐姐前面測(cè)試好好的,測(cè)到這個(gè)工程的時(shí)候,突然跟我反饋,你看這個(gè)這個(gè)工程跑著跑著就不動(dòng)了,日志什么也沒(méi)了。

那時(shí)候正在忙,想著我就沒(méi)改幾行代碼,也沒(méi)涉及核心邏輯,那肯定沒(méi)問(wèn)題的。

于是回復(fù)小姐姐,業(yè)務(wù)邏輯執(zhí)行的太慢了吧,再等個(gè)半小時(shí)再看看?

一小時(shí)后,小姐姐又來(lái)找我,我都等了一小時(shí),這個(gè)工程還是沒(méi)動(dòng)啊,日志還是沒(méi)有啊。

這下不能拖了,上去仔細(xì)一看,還真是,怎么就沒(méi)了呢?

先簡(jiǎn)單說(shuō)下這段代碼,就是使用一個(gè)異步線程執(zhí)行一段業(yè)務(wù)邏輯,示例代碼如下:

  1. // 前置邏輯 
  2. ..... 
  3. Thread thread=new Thread(new Runnable() { 
  4.     @Override 
  5.     public void run() { 
  6.         try { 
  7.            // 異步線程執(zhí)行其他業(yè)務(wù)邏輯 
  8.         } catch (Exception e) { 
  9.            // 不進(jìn)行任何代碼處理 
  10.         } 
  11.     } 
  12. }); 
  13. thread.start(); 

憑著老程序員的經(jīng)驗(yàn),猜到可能是異步線程內(nèi)發(fā)生了異常,導(dǎo)致異步線程退出,不再繼續(xù)執(zhí)行。而又因?yàn)樯鲜龃a「吃掉」了異常,這就導(dǎo)致我們從外部看起來(lái)這個(gè)工程跑著跑著就不動(dòng)了,日志什么也沒(méi)了。

于是改造了一下,打印出相關(guān)異常日志,最終定位問(wèn)題,原來(lái)是小姐姐造的數(shù)據(jù)存在問(wèn)題,從而引發(fā) NPE 問(wèn)題。

「不知道大家有沒(méi)有碰到過(guò)上面的情況,使用線程異步執(zhí)行相關(guān)邏輯,但是執(zhí)行到一半突然就像卡主一般,不再繼續(xù)往下執(zhí)行。」

小黑哥碰到過(guò)幾次,這幾次原因都不太相同,總結(jié)起來(lái)分為下面三種情況:

  • 異步任務(wù)長(zhǎng)時(shí)間被阻塞
  • 異步任務(wù)發(fā)生異常
  • 異步任務(wù)異常被吃掉

異步任務(wù)長(zhǎng)時(shí)間被阻塞

第一種,異步線程執(zhí)行任務(wù),這個(gè)任務(wù)需要通過(guò)網(wǎng)絡(luò)調(diào)用其他遠(yuǎn)端服務(wù)。假設(shè)服務(wù)端響應(yīng)的非常慢,而我們?cè)O(shè)置的網(wǎng)絡(luò)超時(shí)時(shí)間又很長(zhǎng),這就會(huì)導(dǎo)致這個(gè)線程長(zhǎng)時(shí)間被阻塞。

假設(shè)異步任務(wù)偽碼如下:

  1. ThreadPoolExecutor threadPool= ....; 
  2. threadPool.execute(() -> { 
  3. // 1.調(diào)用遠(yuǎn)端服務(wù) 
  4. Socket socket....; 
  5. // 2.設(shè)置超時(shí)時(shí)間 
  6. socket.setSoTimeout(60*1000); 
  7. // 3.讀取服務(wù)端返回 
  8. socket.read(); 
  9. }); 

上面程序中,如果服務(wù)端一直沒(méi)有返回,那么異步線程將會(huì)一直被阻塞,直到超時(shí)。

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

舉一個(gè)極端的例子,假設(shè)上面的代碼沒(méi)有設(shè)置超時(shí)時(shí)間,而服務(wù)端一直沒(méi)有返回響應(yīng),「此時(shí)異步線程就會(huì)被一直阻塞」。

除了上面網(wǎng)絡(luò)讀取阻塞的例子,常見(jiàn)情況還有

  • 執(zhí)行了長(zhǎng)時(shí)間休眠,比如 TimeUnit.MINUTES.sleep(60)
  • 內(nèi)部發(fā)生了死鎖
  • 等等

如果異步線程長(zhǎng)時(shí)間被阻塞,而異步任務(wù)執(zhí)行又比較頻繁,那么線程池內(nèi)可用線程將會(huì)被慢慢耗盡,此時(shí)后續(xù)任務(wù)就會(huì)被拒絕執(zhí)行。

解決辦法

其實(shí)非常簡(jiǎn)單,首先我們使用 jstack 命令 「dump」 一下當(dāng)前 Java 應(yīng)用的線程堆棧情況,然后根據(jù)線程池名字定位相關(guān)線程即可。

網(wǎng)上隨便找了堆棧圖

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

所以創(chuàng)建線程池建議自定義 ThreadFactory 參數(shù),這對(duì)于后期排查問(wèn)題非常有用。

異步任務(wù)異常未捕獲

上面的情況,異步線程其實(shí)還活著,只是被阻塞沒(méi)辦法執(zhí)行后續(xù)的邏輯。

那這一類情況呢,與上面不太一樣,由于異步任務(wù)內(nèi)部發(fā)生錯(cuò)誤,拋出異常,而代碼邏輯中又沒(méi)有進(jìn)行捕獲處理,從而導(dǎo)致線程提前異常退出。

異常退出偽碼如下:

  1. // 1.創(chuàng)建執(zhí)行的任務(wù) 
  2. Runnable runnable=new Runnable() { 
  3.     @Override 
  4.     public void run() { 
  5.        // 執(zhí)行前置邏輯 
  6.         // 拋出異常 
  7.         int i=100/0; 
  8.        // 執(zhí)行后置邏輯 
  9.          
  10.     } 
  11. }; 
  12. // 2.創(chuàng)建線程 
  13. Thread thread=new Thread(runnable); 
  14. // 3.運(yùn)行異步線程 
  15. thread.start(); 
  16. // 其他業(yè)務(wù)邏輯 

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

「異步線程內(nèi)拋出的異常日志僅僅只會(huì)被打印到控制臺(tái),而不會(huì)被記錄到日志文件中。」

所以正常的業(yè)務(wù)日志中是見(jiàn)不到線程異常的日志,這就給了我們一種假象,異步線程看起來(lái)還在執(zhí)行任務(wù),其實(shí)它已經(jīng)掛了。

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

而如果我們?cè)?Linux 機(jī)器上執(zhí)行這段程序,異常日志僅僅只會(huì)顯示在當(dāng)前終端窗口上,一旦關(guān)閉當(dāng)前終端窗口,日志就沒(méi)。了。

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

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

解決辦法

第一種解決辦法,其實(shí)很多讀者已經(jīng)想到了,異步線程內(nèi)使用 try..catch 語(yǔ)句捕獲所有異常即可。

「沒(méi)錯(cuò),就是這么簡(jiǎn)單。」

不過(guò)這里提一點(diǎn),一般我們使用 try..catch僅僅只會(huì)捕獲 Exception異常。

那么極端情況下,異步線程內(nèi)如果拋出 Error,比如拋出了 java.lang.NoClassDefFoundError,此時(shí)是沒(méi)法捕獲,異步線程依舊會(huì)異常退出。

所以我們可以使用try..catch捕獲 Throwable,這樣及時(shí)發(fā)生 Error錯(cuò)誤,也會(huì)被捕獲。

不過(guò)個(gè)人覺(jué)得捕獲Exception異常就夠了,正常工程應(yīng)用很少會(huì)發(fā)生 Error錯(cuò)誤,所以我們只要了解有這個(gè)可能即可。

ps:之前同事上線一個(gè)應(yīng)用,使用異步線程執(zhí)行任務(wù),每次執(zhí)行到一半,都不再繼續(xù)執(zhí)行。

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

最后,小黑哥排查 stdout 輸出日志,才發(fā)現(xiàn)異步線程發(fā)生 Error錯(cuò)誤。

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

一旦設(shè)置完成,如果異步線程內(nèi)發(fā)生異常,線程退出之前將會(huì)調(diào)用異常處理方法。

我們拿 Thread 來(lái)講,其設(shè)置方法如下:

  1. Runnable runnable=new Runnable() { 
  2.     @Override 
  3.     public void run() { 
  4.         int i=100/0; 
  5.     } 
  6. }; 
  7.  
  8. Thread thread=new Thread(runnable); 
  9. thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 
  10.     @Override 
  11.     public void uncaughtException(Thread t, Throwable e) { 
  12.         System.out.println(t.getName()+"發(fā)生異常"+e.getMessage()); 
  13.     } 
  14. }); 
  15. thread.start(); 

不過(guò)生產(chǎn)環(huán)境不建議直接使用 Thread,我們需要使用線程池代替。

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

  1. ThreadPoolExecutor threadPool =new ThreadPoolExecutor( 
  2.         5, 
  3.         10, 
  4.         60, 
  5.         TimeUnit.SECONDS,new ArrayBlockingQueue<>(100), 
  6.       // 這里使用 Guava 的 ThreadFactoryBuilder 類,方便構(gòu)造 ThreadFactory 
  7.         new ThreadFactoryBuilder().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 
  8.             @Override 
  9.             public void uncaughtException(Thread t, Throwable e) { 
  10.                 // 處理異常 
  11.             } 
  12.         }).build() 
  13.         ); 

如果你當(dāng)前使用 ThreadPoolExecutor#submit執(zhí)行異步任務(wù),那就簡(jiǎn)單了,我們可以直接通過(guò) Future#get獲取到線程內(nèi)拋出的異常。

  1. Future<?> future = threadPool.submit(new Callable<Object>() { 
  2.     @Override 
  3.     public Object call() throws Exception { 
  4.         return "小黑十一點(diǎn)半"
  5.     } 
  6. }); 
  7.  
  8. try { 
  9.     future.get(); 
  10. } catch (InterruptedException e) { 
  11.     e.printStackTrace(); 
  12. } catch (ExecutionException e) { 
  13.     // 線程內(nèi)拋出異常將會(huì)被封裝在 ExecutionException 內(nèi) 

異步任務(wù)異常被吃掉

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

這種情況具體來(lái)說(shuō)就是異步線程內(nèi)使用 try..catch 語(yǔ)句捕獲了所有異常,但是沒(méi)有在 catch語(yǔ)句中進(jìn)行任何代碼處理。

  1. Thread thread=new Thread(new Runnable() { 
  2.     @Override 
  3.     public void run() { 
  4.         try { 
  5.             int i=100/0; 
  6.         } catch (Exception e) { 
  7.            // 不進(jìn)行任何代碼處理 
  8.         } 
  9.     } 
  10. }); 
  11. thread.start(); 

如上述代碼所示,catch語(yǔ)句中沒(méi)有進(jìn)行任何代碼處理。即使異步線程內(nèi)真發(fā)生了異常,也不會(huì)有任何提示,這個(gè)異常就像被吃掉一般。

總結(jié)

多線程編程原本就比較復(fù)雜,我們需要處理各種問(wèn)題,那今天主要介紹了一下其中的一個(gè)問(wèn)題:

「異步線程突然停止,就像卡主一般,不再繼續(xù)執(zhí)行代碼邏輯,沒(méi)有任何響應(yīng)」

那這類問(wèn)題,小黑哥根據(jù)自己碰到情況,總結(jié)為三類:

  • 異步任務(wù)長(zhǎng)時(shí)間被阻塞
  • 異步任務(wù)異常
  • 異步任務(wù)異常被吃掉。

對(duì)于第一種,我們?cè)诰W(wǎng)絡(luò)編程中及時(shí)設(shè)置超時(shí)時(shí)間,一般都能避免。

對(duì)于第二、第三種情況,這就需要我們建立一個(gè)良好的編程習(xí)慣,使用try..catch 捕獲所有異常,并且 catch塊中一定做一些處理,比如說(shuō)打印相關(guān)日志。

 

責(zé)任編輯:武曉燕 來(lái)源: 小黑十一點(diǎn)半
相關(guān)推薦

2017-01-18 20:29:00

Windows 10啟動(dòng)欄解決方案

2021-12-02 22:27:30

Windows 11Windows微軟

2014-12-26 10:01:04

架構(gòu)

2020-12-17 12:25:54

Java進(jìn)程內(nèi)存

2021-10-15 20:24:19

AI

2017-06-07 15:16:03

Windows 10Windows以太網(wǎng)圖標(biāo)

2021-04-28 17:01:18

Windows 10Windows微軟

2020-12-03 19:49:21

微信表情移動(dòng)應(yīng)用

2024-12-10 12:56:32

馬斯克機(jī)器人Optimus

2021-12-19 07:25:10

Windows 11操作系統(tǒng)微軟

2021-06-03 21:13:03

內(nèi)存Python管理

2024-01-15 16:34:13

模型訓(xùn)練

2023-03-15 16:24:43

云數(shù)據(jù)庫(kù)代碼開(kāi)發(fā)

2021-03-01 06:28:17

微信表情騰訊

2021-10-29 06:46:42

CPU緩存TLB

2021-09-16 18:29:17

CPU緩存虛擬

2023-09-13 07:23:22

顯卡NVIDIAIntel

2011-08-17 15:08:08

windows7任務(wù)欄縮略圖

2015-07-06 09:51:39

2011-11-16 11:12:46

DNSDNS記錄DNS恢復(fù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产精品区二区三区日本 | 欧美一级片在线看 | 伊人色综合久久天天五月婷 | 韩国精品一区 | 操操网站 | 精品中文字幕久久 | www久久爱| 嫩草研究影院 | 欧美成人影院 | 国产午夜精品一区二区三区在线观看 | 久久精品欧美一区二区三区不卡 | 中文字幕1区 | 精品毛片视频 | 国产精品a久久久久 | 91视频a | 91在线看 | xxx国产精品视频 | 成人国产精品一级毛片视频毛片 | 日韩精品一区二区三区第95 | 天天做日日做 | 国产高清久久久 | 视频精品一区二区三区 | 欧美色综合天天久久综合精品 | 午夜精品一区二区三区在线观看 | 日本高清视频在线播放 | 嫩草懂你的影院入口 | 久久久久久综合 | 91精品国产综合久久国产大片 | 亚洲 欧美 激情 另类 校园 | 毛片毛片毛片毛片毛片 | 亚洲免费视频网址 | 国产福利91精品一区二区三区 | 一级毛片视频在线 | 国产精品自拍av | 久久久av中文字幕 | 日本免费小视频 | h视频在线观看免费 | 91视频一区二区三区 | 国产欧美日韩综合精品一区二区 | 在线观看h视频 | 看av片网站 |