(下面的描述和代码摘抄自书目《java编程思想》)
由于线程的本质特性,使得不能捕获从线程中逃逸的异常。一旦异常逃出任务的run()方法,就会向外传播的控制台,除非采取特殊的步骤捕获这种异常。JavaSE5之前,可以使用线程组捕获这些异常,有了JavaSE5之后,可以使用Executor来解决问题。
下面任务总是会抛出异常,并传播到run()的外部,但是在main()中加入try-catch语句是捕获不到异常的:
public class ExceptionThread implements Runnable{
public void run(){
throw new RuntimeException();
}
public static void main(String[] args){
try{
Thread t = Thread.currentThread();
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
}catch(RuntimeException e){
//This statement will not execute !
System.out.println("Exception has been handled!");
}
}
}
运行结果:
为了解决这个问题,需要修改Executor产生线程的方式。Thread.UncaughtExceptionHandler是JavaSE5中的新接口,允许每个Thread对象上附着一个异常处理器。Thread.UncaughtExceptionHandler.uncaughtException()会在线程因未捕获的异常而临近死亡时被调用。为了使用它,在下面代码中创建了一个新类型的ThreadFactory,它将在每个新创建的Thread对象上附着一个Thread.UncaughtExceptionHandler,然后将这个工程传递给Executors创建新的ExecutorService方法:
public class ExceptionThread2 implements Runnable{
public void run(){
Thread t = Thread.currentThread();
System.out.println("run() by "+t);
System.out.println("eh = "+t.getUncaughtExceptionHandler());
throw new RuntimeException();
}
}
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("Caught "+e);
}
}
public class HandlerThreadFactory implements ThreadFactory{
public Thread newThread(Runnable r){
System.out.println(this +" creating new Thread");
Thread t = new Thread(r);
System.out.println("created "+t);
t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
System.out.println("eh = "+t.getUncaughtExceptionHandler());
return t;
}
}
public class CaptureUncaughtException {
public static void main(String[] args){
ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
exec.execute(new ExceptionThread2());
}
}
运行结果如下:
从运行结果可以看出,未捕获的异常是通过uncaughtException来捕获的。
上面的实例是按照具体的情况逐个设置处理器。如果要在代码中处处使用相同的异常处理器,更简单的方式是在Thread类中设置一个静态域,并将这个处理器设置为默认的未捕获异常处理器:
public class SettingDefaultHandler {
public static void main(String[] args){
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
}
}
运行结果:
这个处理器只有在不存在线程专有的未捕获异常处理器的情况下使用。