java线程 异常_多线程中的异常处理

1.Thread的默认异常处理

线程不允许抛出未捕获的checked exception(比如sleep时的InterruptedException),也就是说各个线程需要自己把自己的checked exception处理掉。我们可以查看一下Thread类的run()方法声明,方法声明上没有对抛出异常进行任何约束。

//Thread类中

@Overridepublic voidrun() {if (target != null) {

target.run();//实际上直接调用Runnable实例的run方法

}

}//Runnable接口中

public abstract void run();

JVM的这种设计源自于这样一种理念:“线程是独立执行的代码片断,线程的问题应该由线程自己来解决,而不要委托到外部。”基于这样的设计理念,在Java中,线程方法的异常(无论是checked还是unchecked exception),都应该在线程代码边界之内(run方法内)进行try catch并处理掉。换句话说,我们不能捕获从线程中逃逸的异常。

2.未捕获的异常哪去了?

一个异常被抛出后,如果没有被捕获处理,则会一直向上抛。异常一旦被Thread.run() 抛出后,就不能在程序中对异常进行捕获,最终只能由JVM捕获。

//不处理异常

public static voidmain(String[] args) {new Thread(newRunnable() {

@Overridepublic voidrun() {int i = 1 / 0;//发生异常

}

}).start();

}//执行结果:

Exception in thread "Thread-0" java.lang.ArithmeticException: /by zero

at thread.thread.thread.Uncaught$1.run

at java.lang.Thread.run======================================================

//尝试对异常进行捕获

public static voidmain(String[] args) {try{new Thread(newRunnable() {

@Overridepublic voidrun() {int i = 1 / 0;//发生异常

}

}).start();

}catch(Exception e) {

System.out.println("捕获线程抛出的异常!");

}

}//执行结果:

Exception in thread "Thread-0" java.lang.ArithmeticException: /by zero

at thread.thread.thread.Uncaught$1.run

at java.lang.Thread.run

上面的例子中,我们尝试在main方法中对线程中抛出的异常进行捕获,但是无济于事。

3.JVM如何处理线程中抛出的异常

查看Thread类的源码,我们可以看到有个dispatchUncaughtException方法,此方法就是用来处理线程中抛出的异常的。JVM会调用dispatchUncaughtException方法来寻找异常处理器(UncaughtExceptionHandler),处理异常。

//向handler分派未捕获的异常。该方法仅由JVM调用。

private voiddispatchUncaughtException(Throwable e) {

getUncaughtExceptionHandler().uncaughtException(this, e);

}//获取用来处理未捕获异常的handler,如果没有设置则返回当前线程所属的ThreadGroup

publicUncaughtExceptionHandler getUncaughtExceptionHandler() {return uncaughtExceptionHandler != null ?uncaughtExceptionHandler : group;

}

UncaughtExceptionHandler必须显示的设置,否则默认为null。若为null,则使用线程默认的handler,即该线程所属的ThreadGroup。ThreadGroup自身就是一个handler,查看ThreadGroup的源码就可以发现,ThreadGroup实现了Thread.UncaughtExceptionHandler接口,并实现了默认的处理方法。默认的未捕获异常处理器处理时,会调用 System.err 进行输出,也就是直接打印到控制台了。

public voiduncaughtException(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)) { //没有配置handler时,默认直接打印到控制台

System.err.print("Exception in thread \""

+ t.getName() + "\" ");

e.printStackTrace(System.err);

}

}

}

由此可知,最终JVM是调用未捕获的异常处理器的uncaughtException()方法来处理异常的,并且是直接打印到控制台。

4.如何自定义处理异常

那么,如果我们要自己处理异常,该怎么办呢?通过前面的分析,我们已经知道了线程会使用默认的未捕获异常处理器来处理异常。自然我们可以想到,是否可以自定义未捕获异常处理器,覆盖掉默认的捕获异常处理器。实际上,Thead确实已经提供了一个setUncaughtExceptionHandler方法,我们只需要将自定义未捕获异常处理器作为参数传入进入就可以了。

public static voidmain(String[] args) {

Thread thread= new Thread(newRunnable() {

@Overridepublic voidrun() {int i = 1 / 0;//发生异常

}

});//自定义未捕获异常处理器

thread.setUncaughtExceptionHandler(newThread.UncaughtExceptionHandler() {

@Overridepublic voiduncaughtException(Thread t, Throwable e) {

System.out.println("尝试捕获线程抛出的异常!");

}

});

thread.start();

}//执行结果:

尝试捕获线程抛出的异常!

5.线程池中自定义处理异常

要自定义处理异常,只需要为线程提供一个自定义的UncaughtExceptionHandler。而在线程池中,该如何批量的为所有线程设置UncaughtExceptionHandler呢?我们知道,线程池中的线程是由线程工厂创建的。我们可以跟踪ThreadPoolExecutor构造方法的源码,最终定位到DefaultThreadFactory类,该类中有个newThread()方法,这就是线程产生的源头了。

public ThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,

TimeUnit unit,

BlockingQueueworkQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,

Executors.defaultThreadFactory(), defaultHandler);

}//Executors类中

public staticThreadFactory defaultThreadFactory() {return newDefaultThreadFactory();

}//DefaultThreadFactory类中

publicThread newThread(Runnable r) {

Thread t= newThread(group, r,

namePrefix+threadNumber.getAndIncrement(),0);if(t.isDaemon())

t.setDaemon(false);if (t.getPriority() !=Thread.NORM_PRIORITY)

t.setPriority(Thread.NORM_PRIORITY);returnt;

}

找到了线程创建的源头,一切就OK了。我们可以实现一个自己的线程工厂,并覆盖默认的newThread方法,为新创建的线程提供一个UncaughtExceptionHandler,即可达到我们的目的。由于DefaultThreadFactory是Executors类的内部类,我们不能直接继承该类,只能实现该工厂类的接口——ThreadFactory接口,来实现我们自己的线程工厂。

class MyThreadFactory implementsThreadFactory {

@OverridepublicThread newThread(Runnable r) {

Thread t= newThread();//自定义UncaughtExceptionHandler

t.setUncaughtExceptionHandler(newMyUncaughtExceptionHandler());returnt;

}

}

需要注意的是,只有通过execute()方法提交任务,才能将它抛出的异常交给未捕获异常处理器。而通过submit()方法提交的任务,无论是抛出未检查异常还是已检查异常,都将被认为是任务返回状态的一部分。如果一个由submit()方法提交的任务由于抛出了异常而结束,该异常将被Future.get()封装在ExecutionException中重新抛出。

参考:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值