由于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());
}
}