持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
多线程下的异常处理
JVM的这种设计源自于这样一种理念:“线程是独立执行的代码片断,线程的问题应该由线程自己来解决,而不要委托到外部。”在Java程序中,无论线程发生checked exception还是unchecked exception异常,都应该在线程代码边界之内(run方法内)进行try catch并处理掉。换句话说,我们不能捕获从线程中逃逸的异常。
在线程内部没有进行异常捕获的话,异常会一直往上抛出,一旦被run方法抛出后,就不能在程序代码中使用try-catch中对异常进行捕获, 最终只能被jvm捕获出来。这时候我们不能对当发生异常时对程序作一些其他的处理。
public class ThreadException {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("线程执行的名称"+Thread.currentThread().getName());
// 模拟发生异常
int a = 1/0;
}
}
接下来我们来修改主线程运行的代码来对子线程MyThread的异常进行捕获。
public class ThreadException {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
} catch (Exception e) {
System.out.println("捕获到了异常"); // 没有输出
e.printStackTrace();
}
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("线程执行的名称"+Thread.currentThread().getName());
// 模拟发生异常
int a = 1/0;
}
}
结果依然和没有进行try-catch的输出一致,这是由于在run方法的异常没有进行捕获,往上抛出后不能再在程序里面进行异常捕获。只能由于jvm来捕获异常,在Thread的类里面有一个dispatchUncaughtException方法,它调用了getUncaughtExceptionHandler获取用来处理未捕获异常的handler,然后向改handler分派未捕获的异常。
// 返回当此线程因未捕获异常而突然终止时调用的处理程序。如果该线程没有显式设置未捕获异常处理程序,则返回该线程的ThreadGroup对象,除非该线程已终止,在这种情况下返回null。
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
return uncaughtExceptionHandler != null ?
uncaughtExceptionHandler : group;
}
// 设置在此线程因未捕获异常而突然终止时调用的处理程序。 通过显式设置其未捕获异常处理程序,线程可以完全控制如何响应未捕获异常。如果没有设置这样的处理程序,那么线程的ThreadGroup对象将充当它的处理程序
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
checkAccess();
uncaughtExceptionHandler = eh;
}
// 将未捕获的异常分派给处理程序。该方法仅由JVM调用
private void dispatchUncaughtException(Throwable e) {
getUncaughtExceptionHandler().uncaughtException(this, e);
}
当未设置uncaughtExceptionHandler的时候getUncaughtExceptionHandler的方法默认返回一个group,使用线程默认的handler,即该线程所属的线程组。ThreadGroup的一个handler,因为他实现Thread.UncaughtExceptionHandler接口,并实现了默认的处理方法,这个方法里主要调用了System.err 进行输出,也就是直接打印到控制台。
public void uncaughtException(Thread t, Throwable e) {
if (parent != null) {
parent.uncaughtException(t, e);
} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
System.err.print("Exception in thread ""
+ t.getName() + "" ");
e.printStackTrace(System.err);
}
}
}
自定义处理线程异常
public class ThreadException {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.setUncaughtExceptionHandler();
thread.start();
} catch (Exception e) {
System.out.println("捕获到了异常"); // 不执行
e.printStackTrace();
}
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("线程执行的名称"+Thread.currentThread().getName());
// 模拟发生异常
int a = 1/0;
}
public void setUncaughtExceptionHandler() {
this.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕获线程里面run方法抛出的异常:"+e.getMessage());
}
});
}
}
线程池中的线程异常
自定义捕获线程池中的异常,需要知道线程池中的线程是如何产生的,然后对每一个Thread设置自定义的异常处理。有一个默认的线程工厂负责生产一个个的线程,Executors.defaultThreadFactory()
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
源代码如上所示,由于DefaultThreadFactory是Executors类的内部类,我们不能直接继承该类,只能实现该工厂类的接口——ThreadFactory接口,来实现我们自己的线程工厂。
class MyDefaultThreadFactory implements ThreadFactory{
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
MyDefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
if (t.isDaemon()) {
t.setDaemon(false);
}
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕获线程抛出的异常:" + e.getMessage());
}
}