由于Java线程的本质特征,当抛出异常的时候就终止了如果再进行catch,都不在一个线程里面,所以无法捕捉到异常。
Java线程中,要在run()方法中把一切的异常都处理掉,可以使用try-catch块。不能让这个线程抛出异常,因为如果我们不使用特殊的方式的话,我们是无法捕获从这个线程中逃逸的异常的。异常一旦抛出了,那么这个线程就会停止运行,但是不会影响主线程和其它的线程。因为主线程和其它的线程都不知道它抛出了异常。
先给出一个例子,线程在run()方法中抛出异常,看main函数能不能catch到。
ExceptionThread.java
package thread.uncaughtException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by louyuting on 2017/3/11.
*/
public class ExceptionThread implements Runnable{
@Override
public void run() {
System.out.println("准备抛出异常");
throw new RuntimeException();
}
public static void main(String[] args) {
Thread thread = new Thread(new ExceptionThread());
ExecutorService service = Executors.newCachedThreadPool();
service.execute(thread);
}
}
看看运行结果:
Exception in thread "pool-1-thread-1" java.lang.RuntimeException
at thread.uncaughtException.ExceptionThread.run(ExceptionThread.java:14)
at java.lang.Thread.run(Thread.java:745)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
准备抛出异常
明显就是没有catch到异常。 难道是因为我们没有再main函数里面用try catch块包围起来?接下来再做测试:
package thread.uncaughtException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by louyuting on 2017/3/11.
*/
public class ExceptionThread implements Runnable{
@Override
public void run() {
System.out.println("准备抛出异常");
throw new RuntimeException();
}
public static void main(String[] args) {
Thread thread = new Thread(new ExceptionThread());
try {
ExecutorService service = Executors.newCachedThreadPool();
service.execute(thread);
} catch (Exception e) {
System.out.println("我捕捉到异常了");
e.printStackTrace();
}
}
}
运行结果:
Exception in thread "pool-1-thread-1" java.lang.RuntimeException
at thread.uncaughtException.ExceptionThread.run(ExceptionThread.java:14)
at java.lang.Thread.run(Thread.java:745)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
准备抛出异常
运行结果还是一样,说明并不是因为这个原因。
想想一个线程的抛出异常之后就终止了如果再进行catch,都不在一个线程里面。
为了解决这个问题,这个时候我们得去实现UncaughtExceptionHandler这个接口来捕获抛出的异常。UncaughtExceptionHandler是JDK5中的新接口,允许我们在每个线程上面都附加一个异常处理器,Thread.UncaughtExceptionHandler.uncaughtException()方法会在线程因未捕捉的异常而在临近死亡时被调用。为了使用这个异常处理器我们创建一个新的线程工厂ThreadFactory。并在new线程时设置UncaughtExceptionHandler。
HandlerThreadFactory.java
package thread.uncaughtException;
import java.util.concurrent.ThreadFactory;
/**
* Created by louyuting on 2017/3/11.
*/
public class HandlerThreadFactory 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;
}
}
这里创建两个抛出异常的线程类
public class ExceptionThread2 implements Runnable {
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println("run by " + t.getName());
System.out.println("Exception " + t.getUncaughtExceptionHandler());
throw new RuntimeException();
}
}
public class ExceptionThread3 implements Runnable {
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println("run by " + t.getName());
System.out.println("Exception " + t.getUncaughtExceptionHandler());
throw new NullPointerException();
}
}
实现我自己的异常处理器MyUncaughtExceptionHandler.java
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("\n [caugth:] " + e.toString());
}
}
测试主函数:
public class CaptureUncaughtException {
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool(new HandlerThreadFactory());
es.execute(new ExceptionThread2());
es.execute(new ExceptionThread3());
}
}
运行结果是:
可知此时捕捉到了异常。
如果你知道将要在代码中处处使用相同的异常处理器,那么更加简单的方式是在Thread类中设置一个静态域,并将这个处理器设置为默认的未捕获异常的处理器。
public class SettingDefaultHandler {
public static void main(String[] args) {
//设置静态异常处理器
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ExceptionThread2());
}
}
小结:
说明:这个处理器只有在不存在线程专有的未捕获异常的情况下才会被调用。系统会检查线程的专有的版本,如果没有发现,则检查线程组是有有其专有的uncaughtException()方法。如果也没有,在调用defaultUncaughtExcept。
在java多线程程序中,所有线程都不允许抛出未捕获的checked exception(比如sleep时的InterruptedException),也就是说各个线程需要自己把自己的checked exception处理掉。这一点是通过java.lang.Runnable.run()方法声明(因为此方法声明上没有throw exception部分)进行了约束。但是线程依然有可能抛出unchecked exception(如运行时异常),当此类异常跑抛出时,线程就会终结,而对于主线程和其他线程完全不受影响,且完全感知不到某个线程抛出的异常(也是说完全无法catch到这个异常)。JVM的这种设计源自于这样一种理念:线程是独立执行的代码片断,线程的问题应该由线程自己来解决,而不要委托到外部。”“基于这样的设计理念,在Java中,线程方法的异常(无论是checked还是unchecked exception),都应该在线程代码边界之内(run方法内)进行try catch并处理掉.换句话说,我们不能捕获从线程中逃逸的异常。