環(huán)保公司網(wǎng)站模板聯(lián)合早報(bào) 即時(shí)消息
目錄
前言
正文
1.線程出現(xiàn)異常的默認(rèn)行為?
2.使用 setUncaughtExceptionHandler() 方法進(jìn)行異常處理
3.使用?setDefaultUncaughtExceptionHandler() 方法進(jìn)行異常處理?
4.線程組內(nèi)處理異常?
5.線程異常處理的優(yōu)先性?
總結(jié)
前言
在緊密交織的多線程環(huán)境中,異常處理是一個(gè)經(jīng)常被討論的容易被忽視的關(guān)鍵部分。這并不奇怪,因?yàn)樵诰帉懖l(fā)代碼時(shí),管理和理解可能出現(xiàn)的各種異常條件可能是一個(gè)挑戰(zhàn)。在單線程環(huán)境中,發(fā)生異常時(shí),異常信息會(huì)立刻被捕獲并處理,然而,在多線程環(huán)境中的異常處理復(fù)雜性要高很多。未被正確處理的異??赡軐?dǎo)致全局性影響,甚至系統(tǒng)崩潰。這給程序穩(wěn)定性帶來威脅,且可能會(huì)導(dǎo)致無法預(yù)料的行為。因此,對(duì)于編寫健壯且可靠的并發(fā)代碼來說,理解并且正確處理線程中的異常是至關(guān)重要的。本文旨在深入探討Java中線程級(jí)別的異常處理。我將會(huì)詳細(xì)闡述線程異常的基本概念,錯(cuò)誤處理策略以及如何用實(shí)際代碼進(jìn)行演示。希望本文能為你在并發(fā)編程中如何捕獲和處理異常提供有用的參考。
正文
當(dāng)線程出現(xiàn)異常時(shí),我們可以在該線程 run() 方法的 catch 語句中進(jìn)行處理。當(dāng)有多個(gè)線程中出現(xiàn)異常時(shí),我們就得在每一個(gè)線程 run() 方法得 catch 語句中進(jìn)行處理,這樣回造成代碼嚴(yán)重冗余。我們可以使用?setDefaultUncaughtExceptionHandler() 和 setUncaughtExceptionHandler() 方法來集中處理線程的異常。?
1.線程出現(xiàn)異常的默認(rèn)行為?
創(chuàng)建測試用例
package org.example.Error;public class threadCreateException {static class MyThread extends Thread{@Overridepublic void run() {String username= null;//NUllPointErrorSystem.out.println(username.hashCode());}}public static void main(String[] args) {MyThread thread = new MyThread();thread.start();}
}
運(yùn)行結(jié)果
程序運(yùn)行后,控制臺(tái)輸出空指針異常。在 Java 的多線程技術(shù)中,我們可以對(duì)多線程中的異常進(jìn)行"捕捉"(使用的是 UncaughtExceptionHander 接口),從而對(duì)異常進(jìn)行有效處理。
當(dāng)線程出現(xiàn)異常而終止時(shí),JVM 虛擬機(jī)捕獲到此情況,并自動(dòng)調(diào)用 UncaughtExceptionHandler 接口中的 void uncaughtException(Thread t,Throwable e) 方法來處理異常,使對(duì)多個(gè)線程的異常處理更加集中。
2.使用 setUncaughtExceptionHandler() 方法進(jìn)行異常處理
新建測試用例
package org.example.Error;
import java.lang.Thread.UncaughtExceptionHandler;
public class Main {static class MyThread extends Thread{@Overridepublic void run() {String username= null;//NUllPointErrorSystem.out.println(username.hashCode());}}public static void main(String[] args) throws InterruptedException {MyThread t1 = new MyThread();t1.setName(" 線程 t1");t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println("線程:"+t.getName()+" 出現(xiàn)了異常:");e.printStackTrace();}});t1.start();MyThread t2 = new MyThread();t2.setName(" 線程 t2");Thread.sleep(10);t2.start();}}
程序運(yùn)行結(jié)果如圖
setUncaughtExceptionHandler() 方法的作用是對(duì)指定的線程對(duì)象設(shè)置默認(rèn)的異常處理器,在 Thread 類中,我們還可以使用?setDefaultUncaughtExceptionHandler() 方法對(duì)所有線程對(duì)象設(shè)置異常處理器。
3.使用?setDefaultUncaughtExceptionHandler() 方法進(jìn)行異常處理?
新建測試用例?
package org.example.Error;public class Main3 {static class MyThread extends Thread{@Overridepublic void run() {String username= null;//NUllPointErrorSystem.out.println(username.hashCode());}}public static void main(String[] args) {MyThread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println("線程:"+t.getName()+" 出現(xiàn)了異常:");e.printStackTrace();}});MyThread t1 = new MyThread();t1.setName(" 線程t1 ");t1.start();MyThread t2 = new MyThread();t2.setName(" 線程t2 ");t2.start();}
}
運(yùn)行結(jié)果如下
4.線程組內(nèi)處理異常?
線程的異常處理情況我們已經(jīng)了解,那么線程組內(nèi)的異常處理情況又是怎么樣的呢??
package org.example.Error;public class thread_Group1 {static class MyThread extends Thread {private String num;public MyThread(ThreadGroup group, String name, String num) {super(group, name);this.num = num;}@Overridepublic void run() {int numInt = Integer.parseInt(num);while (true) {System.out.println("死循環(huán)中: " + Thread.currentThread().getName());}}}public static void main(String[] args) {ThreadGroup group = new ThreadGroup("我得線程組");MyThread[] myThreads = new MyThread[10];for (int i = 0; i < myThreads.length; i++) {myThreads[i] = new MyThread(group,"線程 "+(i+1),"1");myThreads[i].start();}MyThread newT = new MyThread(group,"報(bào)錯(cuò)線程 ","a");newT.start();}
}
運(yùn)行結(jié)果如圖:
從運(yùn)行結(jié)果來看,默認(rèn)的情況下線程組中的一個(gè)線程出現(xiàn)異常后不會(huì)影響其他線程的運(yùn)行。
如何實(shí)現(xiàn)線程組內(nèi)一個(gè)線程出現(xiàn)異常后全部線程都停止呢?
class MyThreadGroup extends ThreadGroup {public MyThreadGroup(String name) {super(name);}@Overridepublic void uncaughtException(Thread t, Throwable e) {super.uncaughtException(t, e);this.interrupt();}}
注意,使用 this 關(guān)鍵字停止線程 。this 代表的是線程組!
public void uncaughtException(Thread t, Throwable e)? 方法的 t 參數(shù)是出現(xiàn)異常的線程對(duì)象。
類 MyThread.java 的代碼如下:?
static class MyThread extends Thread {private String num;public MyThread(ThreadGroup group, String name, String num) {super(group, name);this.num = num;}@Overridepublic void run() {int numInt = Integer.parseInt(num);while (true) {if (!this.isInterrupted()){System.out.println("死循環(huán)中: " + Thread.currentThread().getName());}}}}
需要注意的是,使用自定義 java.lang.ThreadGroup 線程組,并重寫 uncaughtException() 方法處理組內(nèi)線程中斷行為時(shí),每個(gè)線程對(duì)象中的 run() 方法內(nèi)部不要有異常 catch 語句。如果有 catch 語句?,public void uncaughtException(Thread t, Throwable e) 方法不執(zhí)行。
main 方法如下:
public static void main(String[] args) throws InterruptedException {MyThreadGroup group = new MyThreadGroup("我的線程組");MyThread[] myThreads = new MyThread[10];for (int i = 0; i < myThreads.length; i++) {myThreads[i] = new MyThread(group," 線程 "+(i+1),"1");myThreads[i].start();}MyThread newT = new MyThread(group,"報(bào)錯(cuò)線程","a");newT.start();}
運(yùn)行后一個(gè)線程出現(xiàn)異常,其他線程全部停止,運(yùn)行結(jié)果如圖:
5.線程異常處理的優(yōu)先性?
前面介紹了若干個(gè)線程異常處理方式,這些方式如果一起運(yùn)行,會(huì)出現(xiàn)什么運(yùn)行結(jié)果呢??
創(chuàng)建測試用例:?
public class threadExceptionMove {//線程類static class MyThread extends Thread{private String num = "a";public MyThread() {}public MyThread(ThreadGroup group, String name) {super(group, name);}@Overridepublic void run() {int numInt = Integer.parseInt(num);System.out.println(" 在線程中打印: "+(numInt+1));}}//線程組的異常處理static class MyThreadGroup extends ThreadGroup{public MyThreadGroup(String name) {super(name);}@Overridepublic void uncaughtException(Thread t, Throwable e) {super.uncaughtException(t, e);System.out.println("線程組的異常處理");e.printStackTrace();}}//對(duì)象的異常處理static class ObjectUncaughtExceptionHandler implements UncaughtExceptionHandler{@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println(" 對(duì)象的異常處理 ");e.printStackTrace();}}//靜態(tài)的異常處理static class StateUncaughtExceptionHandler implements UncaughtExceptionHandler{@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println("靜態(tài)的異常處理");e.printStackTrace();}}
}
創(chuàng)建運(yùn)行類 Run1:
static class Run1{public static void main(String[] args) {MyThread myThread = new MyThread();//對(duì)象myThread.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler());//類MyThread.setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler());myThread.start();}}
運(yùn)行結(jié)果如圖:
更改 Run1:
static class Run1{public static void main(String[] args) {MyThread myThread = new MyThread();//對(duì)象//smyThread//.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler());//類MyThread.setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler());myThread.start();}}
運(yùn)行結(jié)果如圖:
再繼續(xù)實(shí)驗(yàn),創(chuàng)建 Run2
static class Run2 {public static void main(String[] args) {MyThreadGroup myThreadGroup = new MyThreadGroup("我的線程組");MyThread myThread = new MyThread(myThreadGroup, "我的線程");//對(duì)象myThread.setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler());//類MyThread.setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler());myThread.start();}}
運(yùn)行結(jié)果如圖:
更改 Run2:
static class Run2 {public static void main(String[] args) {MyThreadGroup myThreadGroup = new MyThreadGroup("我的線程組");MyThread myThread = new MyThread(myThreadGroup, "我的線程");//對(duì)象//myThread// .setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler());//類MyThread.setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler());myThread.start();}}
運(yùn)行結(jié)果如圖:
本示例想要打印 "靜態(tài)的異常處理" 信息,必須在??public void uncaughtException(Thread t, Throwable e) 方法中加上?super.uncaughtException(t, e); 代碼。這是因?yàn)?super.uncaughtException(t, e); 方法中會(huì)調(diào)用?Thread.getDefaultUncaughtExceptionHandler();來執(zhí)行 設(shè)置的靜態(tài)方法。源代碼如下:
//super.uncaughtException(t, e);public void uncaughtException(Thread t, Throwable e) {if (parent != null) {parent.uncaughtException(t, e);} else {//此處Thread.UncaughtExceptionHandler ueh =Thread.getDefaultUncaughtExceptionHandler();if (ueh != null) {ueh.uncaughtException(t, e);} else if (!(e instanceof ThreadDeath)) {System.err.print("Exception in thread \""+ t.getName() + "\" ");e.printStackTrace(System.err);}}}
繼續(xù)更改 Run2 代碼
static class Run2 {public static void main(String[] args) {MyThreadGroup myThreadGroup = new MyThreadGroup("我的線程組");MyThread myThread = new MyThread(myThreadGroup, "我的線程");//對(duì)象//myThread// .setUncaughtExceptionHandler(new ObjectUncaughtExceptionHandler());//類//MyThread.// setDefaultUncaughtExceptionHandler(new StateUncaughtExceptionHandler());myThread.start();}}
運(yùn)行結(jié)果如圖:
最終得出結(jié)論就是如果調(diào)用?setUncaughtExceptionHandler() 方法,則其設(shè)置的異常處理器優(yōu)先運(yùn)行,其他異常處理器不運(yùn)行。
總結(jié)
當(dāng)線程出現(xiàn)異常時(shí),如果該異常沒有被捕獲和處理,線程就會(huì)終止。正確處理線程中的異常是保證程序健壯性和穩(wěn)定性的關(guān)鍵所在。
處理線程中的異常通常有兩種方式:一種是使用 try-catch 塊捕獲異常,第二種是使用線程的未捕獲異常處理器(UncaughtExceptionHandler)來處理未捕獲的異常。
在使用 try-catch 塊捕獲異常時(shí),我們可以通過捕獲并處理異常來保證程序的穩(wěn)定性,應(yīng)該將 catch 塊放置在可能出現(xiàn)異常的代碼塊之后,防止程序因未處理的異常而崩潰。在捕獲到異常后,可以在 catch 塊中采取相應(yīng)的措施,比如記錄日志或者返回默認(rèn)值等。
而在使用線程的未捕獲異常處理器時(shí),我們需要通過實(shí)現(xiàn)?
UncaughtExceptionHandler
?接口,并將其設(shè)置到對(duì)應(yīng)線程上,當(dāng)線程發(fā)生未捕獲異常時(shí),處理器中的?uncaughtException
?方法會(huì)被回調(diào)。在該方法中,我們可以采取相應(yīng)的措施來處理異常,比如記錄日志或者發(fā)送警報(bào)等。當(dāng)出現(xiàn)異常時(shí),如何選擇捕獲和處理異常的方式取決于具體情況,但處理異常的目標(biāo)總是相同的,即保障程序能夠繼續(xù)穩(wěn)定執(zhí)行。因此,對(duì)于線程中的異常,我們需要充分了解其出現(xiàn)的原因和可能的影響,制定相應(yīng)的處理策略,并積極監(jiān)控和記錄程序運(yùn)行時(shí)的異常情況。
總之,對(duì)線程中出現(xiàn)的異常進(jìn)行正確處理是保證程序健壯性和穩(wěn)定性的重要措施,有助于保證程序順利運(yùn)行并最大限度地避免非預(yù)期的錯(cuò)誤。