用Thread中的UncaughtExceptionHandler来处理未捕获的异常

Java中在处理异常的时候,一般的作法是使用try-catch-finally来包含代码块,可是Java自身还有一种方式能够处理——使用UncaughtExceptionHandler。它能检测出某个线程因为未捕获的异常而终结的状况。当一个线程因为未捕获异常而退出时,JVM会把这个事件报告给应用程序提供的UncaughtExceptionHandler异常处理器(这是Thread类中的接口):java

//Thread类中的接口
public interface UncaughtExceptionHanlder {
	void uncaughtException(Thread t, Throwable e);
}

在Java 5之后,能够经过如下实例方法来为每一个线程设置一个UncaughtExceptionHandler:ide

Thread.setUncaughtExceptionHandler(UncaughtExceptionHandler handler);//实例方法

或者经过如下静态方法来设置默认的UncaughtExceptionHandler:函数

Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler);//静态方法

这些异常处理器中,只有一个将会被调用——JVM首先搜索每一个线程的异常处理器,若没有,则搜索该线程的ThreadGroup的异常处理器。ThreadGroup中的默认异常处理器实现是将处理工做逐层委托给上层的ThreadGroup,直到某个ThreadGroup的异常处理器可以处理该异常,不然一直传递到顶层的ThreadGroup。顶层ThreadGroup的异常处理器委托给默认的系统处理器(若是默认的处理器存在,默认状况下为空),不然把栈信息输出到System.err。下面是一个Example:this

import java.lang.Thread.UncaughtExceptionHandler;

public class T {
	public static void main(String[] args) throws Exception {
		//为全部线程设置默认的未捕捉异常处理器
		Thread.setDefaultUncaughtExceptionHandler(new MyDefaultExceptionHandler());
		Thread.currentThread().setName("Main Thread");
		
		Thread thread = new Thread(new MyTask("MyTask"), "Child Thread");
		//为某个线程单独设置异常处理器
		thread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
		thread.start();
		//主线程抛出异常,将会使用默认的异常处理器
		throw new RuntimeException("IllegalArgumentException");
	}
}

class MyDefaultExceptionHandler implements UncaughtExceptionHandler {
	@Override
	public void uncaughtException(Thread t, Throwable e) {
		System.out.println("MyDefaultExceptionHandler: Thread: " + 
				t.getName() + ", Message: " + e.getMessage());
	}
}
class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {
	@Override
	public void uncaughtException(Thread t, Throwable e) {
		System.out.println("MyUncaughtExceptionHandler: Thread: " + 
				t.getName() + ", Message: " + e.getMessage());
	}
}
class MyTask implements Runnable {
	private String name;
	public MyTask(String name) {
		this.name = name;
	}
	public MyTask(){}
	public String getName() {
		return name;
	}
	@Override
	public void run() {
		throw new RuntimeException(name + " gets a NullPointerException");
	}
}

执行结果:spa

MyDefaultExceptionHandler: Thread: Main Thread, Message: IllegalArgumentException
MyUncaughtExceptionHandler: Thread: Child Thread, Message: MyTask gets a NullPointerException

能够看到,Main Thread因为没有显式设置UncaughtExceptionHandler,其抛出的未捕获异常,被默认异常处理器MyDefaultUncaughtExceptionHandler处理了,而Child Thread因为单独设置了UncaughtExceptionHanlder,其抛出的未捕获异常,则被Thread自己自带的MyUncaughtExceptionHandler处理了。线程

若是要为线程池中的全部线程设置一个UncaughtExceptionHandler,则须要为ThreadPoolExecutor的构造函数提供一个自定义的ThreadFactory(与全部的线程操控同样,只有线程的全部者可以改变线程的UncaughtExceptionHandler):code

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class T {
	public static void main(String[] args) throws Exception {
		//使用自定义的ThreadFactory来建立线程,并绑定同一个异常处理器
		UncaughtExceptionHandler handler = new MyUncaughtExceptionHandler();
		ExecutorService executor = Executors.newCachedThreadPool(new MyThreadFactory(handler));
		executor.execute(new MyTask("task1"));
		executor.execute(new MyTask("task2"));
		executor.execute(new MyTask("task3"));
		executor.shutdown();
	}
}
class MyTask implements Runnable {
	private String name;
	public MyTask(String name) {
		this.name = name;
	}
	public MyTask(){}
	public String getName() {
		return name;
	}
	@Override
	public void run() {
		throw new RuntimeException(name + " gets a NullPointerException");
	}
}
class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {
	@Override
	public void uncaughtException(Thread t, Throwable e) {
		System.out.println("MyUncaughtExceptionHandler: Thread: " + 
				t.getName() + ", Message: " + e.getMessage());
	}
}
class MyThreadFactory implements ThreadFactory {
	private UncaughtExceptionHandler handler;
	public MyThreadFactory(UncaughtExceptionHandler handler) {
		this.handler = handler;
	}
	@Override
	public Thread newThread(Runnable r) {
		Thread thread = new Thread(r);
		//在这里设置异常处理器
		thread.setUncaughtExceptionHandler(handler);
		return thread;
	}
}

执行结果:接口

MyUncaughtExceptionHandler: Thread: Thread-0, Message: task1 gets a NullPointerException
MyUncaughtExceptionHandler: Thread: Thread-2, Message: task3 gets a NullPointerException
MyUncaughtExceptionHandler: Thread: Thread-1, Message: task2 gets a NullPointerException

从结果中能够看出,线程池中的每一个线程都使用同一个异常处理器来处理未捕获的异常。事件

不过,上面的结果能证实:经过execute提交的任务,能将它抛出的异常交给未捕获的异常处理器。下面的例子只修改了main方法(其他部分请参考前文),以submit方式提交任务:get

public class T {
	public static void main(String[] args) throws Exception {
		//使用自定义的ThreadFactory来建立线程,并绑定同一个异常处理器
		UncaughtExceptionHandler handler = new MyUncaughtExceptionHandler();
		ExecutorService executor = Executors.newCachedThreadPool(new MyThreadFactory(handler));
		//经过submit方法提交任务
		Future future1 = executor.submit(new MyTask("task1"));
		Future future2 = executor.submit(new MyTask("task2"));
		System.out.println(future1.get());
		System.out.println(future2.get());
		executor.shutdown();
	}
}

执行结果:

Exception in thread "main" java.util.concurrent.ExecutionException: 
java.lang.RuntimeException: task1 gets a NullPointerException
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:188)
	at T.main(T.java:15)
Caused by: java.lang.RuntimeException: task1 gets a NullPointerException
	at MyTask.run(T.java:31)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
	at java.util.concurrent.FutureTask.run(FutureTask.java:262)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:745)

结果告诉咱们,future1.get的时候遇到了ExecutionException。那咱们再来看看Future.get方法的实现(在java.util.concurrent.FutureTask类中):

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)//若是任务没有结束,则等待结束
        s = awaitDone(false, 0L);
    return report(s);//若是执行结束,则报告执行结果
}

@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
    Object x = outcome;
    if (s == NORMAL)//若是执行正常,则返回结果
        return (V)x;
    if (s >= CANCELLED)//若是任务被取消,调用get则报CancellationException
        throw new CancellationException();
    throw new ExecutionException((Throwable)x);//执行异常,则抛出ExecutionException
}

源代码说明:若是一个由submit提交的任务因为抛出了异常而结束,那么这个异常将被Future.get封装在ExecutionException中从新抛出。因此,经过submit提交到线程池的任务,不管是抛出的未检查异常仍是已检查异常,都将被认为是任务返回状态的一部分,所以不会交由异常处理器来处理。