- 先看个例子,体会一下多线程异常捕获的重要性:
在run()方法中手动抛出一个运行时异常,在main中启动线程,catch语句中捕获异常,捕获到打印一句话,运行结果如下:public class ThreadException implements Runnable{
@Override
public void run() {
throw new RuntimeException();
}
//现象:控制台打印出异常信息,并运行一段时间后才停止
public static void main(String[] args){
//就算把线程的执行语句放到try-catch块中也无济于事
try{
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ThreadException());
}catch(RuntimeException e){
System.out.println("Exception has been handled!");
}
}
}
发现异常被抛到了控制台,没有打印catch语句块中的语句。
结论:多线程运行不能按照顺序执行过程中捕获异常的方式来处理异常,异常会被直接抛出到控制台(由于线程的本质,使得你不能捕获从线程中逃逸的异常。 一旦异常逃出任务的run()方法,它就会向外传播到控制台,除非你采用特殊的形式捕获这种异常。)这样会让你很头疼,无法捕捉到异常就无法异常引发的问题。
于是,我们一定会想如何在多线程中捕获异常。
多线程中捕获异常
- 定义异常处理器, 要求实现Thread.UncaughtExceptionHandler的uncaughtException方法,如下:
/*
* 第一步:定义符合线程异常处理器规范的“异常处理器”
* 实现Thread.UncaughtExceptionHandler规范
*/
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
/*
* Thread.UncaughtExceptionHandler.uncaughtException()会在线程因未捕获的异常而临近死亡时被调用
*/
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("caught "+e);
}
ii. 定义使用该异常处理器的线程工厂
/*
* 第二步:定义线程工厂
* 线程工厂用来将任务附着给线程,并给该线程绑定一个异常处理器
*/
class HanlderThreadFactory implements ThreadFactory{
@Override
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;
}
iii. 定义一个任务,让其抛出一个异常
/*
* 第三步:我们的任务可能会抛出异常
* 显示的抛出一个exception
*/
class ExceptionThread 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();
}
}
iv. 调用
/*
* 第四步:使用线程工厂创建线程池,并调用其execute方法
*/
public class ThreadExceptionUncaughtExceptionHandler{
public static void main(String[] args){
ExecutorService exec = Executors.newCachedThreadPool(new HanlderThreadFactory());
exec.execute(new ExceptionThread());
}
}
运行结果如下图:
- 结论
在java中药捕获多线程产生的异常,需要自定义异常处理,并设定到对应的线程工厂中
- 拓展
如果你将要在代码中处处使用相同的异常处理器,那么更简单的方式是在Thread类中设置一个静态域,并将这个处理器设置为默认的未捕获异常处理器。这个处理器只有在不存在线程专有的未捕获异常处理器的情况下才会被调用。
public static void main(String[] args){
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
ExecutorService exec =Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
}