在java多線程程序中,所有線程都不允許拋出未捕獲的checked exception(比如sleep時的InterruptedException),也就是說各個線程需要自己把自己的checked exception處理掉。這一點是通過java.lang.Runnable.run()方法聲明(因為此方法聲明上沒有throw exception部分)進行了約束。但是線程依然有可能拋出unchecked exception(如運行時異常),當此類異常跑拋出時,線程就會終結,而對於主線程和其他線程完全不受影響,且完全感知不到某個線程拋出的異常(也是說完全無法catch到這個異常)。JVM的這種設計源自於這樣一種理念:“線程是獨立執行的代碼片斷,線程的問題應該由線程自己來解決,而不要委托到外部。”基於這樣的設計理念,在Java中,線程方法的異常(無論是checked還是unchecked exception),都應該在線程代碼邊界之內(run方法內)進行try catch並處理掉.換句話說,我們不能捕獲從線程中逃逸的異常。
看下面的例子:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExceptionThread implements Runnable {
@Override
public void run() {
throw new RuntimeException("這個線程就干了這麼一件事,拋出一個運行時異常");
}
public static void main(String[] args) {
try {
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
System.out.println("該干嘛干嘛去");
} catch (RuntimeException e) {
System.out.println("能不能捕獲到異常?");
}
}
}
運行結果:
該干嘛干嘛去
Exception in thread "pool-1-thread-1" java.lang.RuntimeException: 這個線程就干了這麼一件事,拋出一個運行時異常
at ExceptionThread.run(ExceptionThread.java:8)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
從運行結果中,我們可以看到的是,這個異常在main線程中沒有catch到,即
System.out.println("能不能捕獲到異常?");
永遠不會執行到。
問題來了,我們如果需要捕獲其線程的unchecked異常時該怎麼辦?Java SE5之後,我們可以通過Executor來解決這個我問題。為了解決這個問題,我們需要修改Executor產生線程的方式。Thread.UncaughtExceptionHandler是java SE5中的新接口,它允許我們在每一個Thread對象上添加一個異常處理器。(UncaughtExceptionHandler)。Thread.UncaughtExceptionHandler.uncaughtException()方法會在線程因未捕獲的異常而面臨死亡時被調用。下面這個例子簡單的演示了如何使用UncaughtExceptionHandler
public class ExceptionThread2 implements Runnable {
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println("run() by" + t);
System.out.println("eh=" + t.getUncaughtExceptionHandler());
throw new RuntimeException("拋出運行時異常");
}
}
import java.lang.Thread.UncaughtExceptionHandler;
/**
* 用於捕獲異常---捕獲的是uncheckedException
*
* @author February30th
*
*/
public class MyUnchecckedExceptionhandler implements UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕獲到異常:" + e);
}
}
import java.util.concurrent.ThreadFactory;
public class HandlerThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
System.out.println("創建一個新的線程");
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
System.out.println("eh121 = " + t.getUncaughtExceptionHandler());
return t;
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadExceptionTest {
/**
* @param args
*/
public static void main(String[] args) {
//下面有3中方式來執行線程。
//第1種按照普通的方式。這是能捕獲到異常
Thread t = new Thread(new ExceptionThread2());
t.setUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
t.start();
//第2種按照現成池,直接按照thread方式,此時不能捕獲到異常,為什麼呢?因為在下面代碼中創建了一個線程,且設置了異常處理器,
//但是呢,在我們線程池中會重設置新的Thread對象,而這個Thread對象沒有設置任何異常處理器,換句話說,我們在線程池外對線程做的
//任何操作都是沒有用的
ExecutorService exec1 = Executors.newCachedThreadPool();
Runnable runnable = new ExceptionThread2();
Thread t1 = new Thread(runnable);
t1.setUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
exec1.execute(runnable);
//第3種情況一樣的,也是走的線程池,但是呢是通過ThreadFactory方式,在ThreadFactory中會對線程做一些控制,可以設置異常處理器
//此時是可以捕獲異常的。
ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
exec.execute(new ExceptionThread2());
}
}
運行結果
創建一個新的線程
eh121 = MyUnchecckedExceptionhandler@1b8e059
run() byThread[Thread-0,5,main]
eh=MyUnchecckedExceptionhandler@1b8e059
捕獲到異常:java.lang.RuntimeException: 拋出運行時異常
從上述的運行結果中可以看到,未捕獲的異常是通過uncaughtException來捕獲的。
按照上述的實例,我們可以按照具體的情況,逐個地設置處理器。但是如果我們知道將要在代碼的所有地方都是用相同的異常處理器,那麼更簡單的方式是在Thread類中設置一個靜態域,並將這個處理器設置為默認的未捕獲異常處理器。看下面的例子:
Thread.setDefaultUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
exec.execute(new ExceptionThread2());
這個默認的處理器只有在線程不存在非默認的異常處理器時才會調用。 在運行時,系統會檢查線程是否有屬於自己的異常處理器,如果發現沒有,就去檢查相應的線程組是否有專有的異常處理器,如果發現也沒有,再調用默認的異常處理器。
大話設計模式(帶目錄完整版) PDF+源代碼 http://www.linuxidc.com/Linux/2014-08/105152.htm
Java中介者設計模式 http://www.linuxidc.com/Linux/2014-07/104319.htm
Java 設計模式之模板方法開發中應用 http://www.linuxidc.com/Linux/2014-07/104318.htm
設計模式之 Java 中的單例模式(Singleton) http://www.linuxidc.com/Linux/2014-06/103542.htm