
一、場景描述:單線程程序可以用try...catch捕獲程序的異常,而在多線程程序的時候是無法使用try...catch捕獲。
示例1:多線程發生異常,無法使用try...catch捕獲問題
public class NoCaughtThread implements Runnable{
@Override
public void run() {
System.out.println(3 / 2);
System.out.println(3 / 0);
System.out.println(3 / 1);
}
public static void main(String[] args) {
try {
Thread thread = new Thread(new NoCaughtThread());
thread.start();
} catch (Exception e) {
System.out.println("==Exception: " + e.getMessage());
}
}
}
運行結果:
1
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at threadtest.NoCaughtThread.run(NoCaughtThread.java:7)
at java.lang.Thread.run(Thread.java:724)
顯然這並非程序設定異常捕獲,此時try...catch無法捕獲線程的異常。此時,如果線程因為異常而終止執行將無法檢測到異常。究其原因Thread類run()方法是不拋出任何檢查型異常的,而自身卻可能因為一個異常而被中止。
二、解決方式大致有兩種:① 在run()中設置對應的異常處理,主動方法來解決未檢測異常;② Thread類API中提供Interface接口UncaughtExceptionHandler,該接口包含uncaughtException方法,它能檢測出某個未捕獲的異常而終結的情況;
示例2:主動的檢測異常
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class InitiativeCaught {
public static void main(String[] args) {
InitialtiveThread initialtiveThread = new InitialtiveThread() ;
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(initialtiveThread);
exec.shutdown();
}
}
class InitialtiveThread implements Runnable {
@Override
public void run() {
Throwable thrown = null;
try {
System.out.println(3 / 2);
System.out.println(3 / 0);
System.out.println(3 / 1);
} catch (Throwable e) {
thrown = e;
} finally {
threadDeal(this, thrown);
}
}
public void threadDeal(Runnable r, Throwable t) {
System.out.println("==Exception: " + t.getMessage());
}
}
運行結果:
1
==Exception: / by zero
此時是主動捕獲異常並做處理,得到想要的結果。
示例3:Thread類API中提供UncaughtExceptionHandler接口捕獲異常,要求檢測線程異常,發生異常設置為重復調用三次之後結束線程。
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadMonitor implements Runnable {
private int data; // 可設置通過構造傳參
private int control = 0;
private static final int MAX = 3; // 設置重試次數
public ThreadMonitor(int i) {
this.data = i;
}
public ThreadMonitor() {
// TODO Auto-generated constructor stub
}
@Override
public void run() {
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread arg0, Throwable e) {
// TODO Auto-generated method stub
System.out.println("==Exception: " + e.getMessage());
String message = e.getMessage();
if( control==MAX ){
return ;
}else if( "ok".equals(message) ){
return ;
}else if ( "error".equals(message) ) {
new Thread() {
public void run() {
try {
System.out.println("開始睡眠。");
Thread.sleep(1 * 1000);
control++ ;
System.out.println("睡眠結束,control: "+ control);
myTask(data) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
}else{
return ;
}
}
});
myTask(data) ;
}
@SuppressWarnings("finally")
public void myTask(int data){
boolean flag = true ;
try {
System.out.println(4 / data);
} catch (Exception e) {
flag = false ;
} finally {
if( flag ){
throw new RuntimeException("ok");
}else{
throw new RuntimeException("error");
}
}
}
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
ThreadMonitor threadMonitor = new ThreadMonitor(0);
exec.execute(threadMonitor);
exec.shutdown();
}
}
運行結果:
==Exception: error
開始睡眠。
睡眠結束,control: 1
==Exception: error
開始睡眠。
睡眠結束,control: 2
==Exception: error
開始睡眠。
睡眠結束,control: 3
==Exception: error
此時,可以正常捕獲線程因除數為零造成的中斷。其中:
(1) 在Thread類API中提供Interface接口UncaughtExceptionHandler,該接口包含一個uncaughtException方法,它能檢測出某個由於未捕獲的異常而終結的情況。定義如下:
UncaughtExceptionHandler接口: public static interface Thread.UncaughtExceptionHandler
uncaughtException方法: public void uncaughtException(Thread t, Throwable e)
(2) uncaughtException方法會捕獲線程的異常,此時需要覆寫該方法設定自定義的處理方式。
(3) 設置UncaughtExceptionHandler異常處理:
方式一:通過Thread提供的靜態static方法,設置默認異常處理:public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler ux)
方式二:通過方法:public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
(4) UncaughtExceptionHandler異常處理需要設置在run()方法內,否則無法捕獲到線程的異常。
(5) 參考鏈接:
JAVA下內存池啟動程序:http://www.cnblogs.com/zhujiabin/p/5404771.html
JAVA下多線程異常處理:http://blog.csdn.net/u013256816/article/details/50417822