一、概述
为什么要单独讲多线程的异常捕捉呢?先看个例子:
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!"); } } }
在run中手动抛出了一个运行时异常,在main中启动线程,catch语句块中捕捉下异常,捕捉到打印一句话。运行结果如下图:
发现异常被抛到了控制台,没有打印catch块中的语句。
结论:多线程运行不能按照顺序执行过程中捕获异常的方式来处理异常,异常会被直接抛出到控制台(由于线程的本质,使得你不能捕获从线程中逃逸的异常。一旦异常逃逸出任务的run方法,它就会向外传播到控制台,除非你采用特殊的形式捕获这种异常。),这样会让你很头疼,无法捕捉到异常就无法处理异常而引发的问题。
于是,我们一定会想如何在多线程中捕捉异常呢?
二、多线程中捕捉异常
我们来按照下面的步骤完成这次实验:
1.定义异常处理器
要求,实现 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); } }
2.定义使用该异常处理器的线程工厂
/* * 第二步:定义线程工厂 * 线程工厂用来将任务附着给线程,并给该线程绑定一个异常处理器 */ 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; } }
3.定义一个任务,让其抛出一个异常
/* * 第三步:我们的任务可能会抛出异常 * 显示的抛出一个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(); } }
4.调用实验
/* * 第四步:使用线程工厂创建线程池,并调用其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()); }
注:以上代码均来自《thinking in java》,内容均是自己总结,如有错误,欢迎大家批评指正