多线程的异常处理
在线程中如果出现异常想要额外进行一些操作的话,可以使用线程的异常处理机制,UncaughtExceptionHandler,这是线程的一个子接口,当一个未捕获的异常导致线程中断的时候JVM会使用thread.getUncaughtExceptionHandler()来查询线程的uncaughtExceptionHandler并将线程和异常作为参数传递给uncaughtException方法
1 |
|
如果当前线程没有显示的调用thread.setUncaughtExceptionHandler()方法设置处理器时,会默认使用该线程的线程组的uncaughtException()方法,线程组是实现了UncaughtExceptionHandler接口的,线程组是在线程实例化时就进行指定的
1 | public class ThreadGroup implements Thread.UncaughtExceptionHandler |
自定义Handler
如何自定义handler并进行使用呢,那当然是要实现UncaughtExceptionHandler接口了,重写uncaughtException()方法即可
1 | class MyExceptionHandler implements Thread.UncaughtExceptionHandler{ |
在实例化线程的时候进行指定handler即可
1 | thread.setUncaughtExceptionHandler(new MyExceptionHandler()); |
代码示例
1 | public class TestExceptionHandler { |
例子不是一个好例子,但是主要就是展示一下如何使用,场景自己把握
线程池中处理异常
在项目中很少自己去继承Thread来启动线程,更多的是使用线程池,而在线程池中UncaughtExceptionHandler是否生效呢?
在测试时发现,再调用executorService.execute方法的时候uncaughtException方法生效了,而在调用executorService.submit方法的时候,uncaughtException方法没有生效,这个是不是很奇怪
submit方法为什么获取不到异常
其实这是因为线程池中submit方法会将任务使用FutureTask进行包装,然后再将FutureTask对象传递给execute来进行执行
1 | public Future<?> submit(Runnable task) { |
而FutureTask在执行的时候,已经进行了try…catch处理
1 | try { |
在使用FutureTask的get操作来获取结果时
1 | public V get() throws InterruptedException, ExecutionException { |
处理异常
可以有两种方式来处理异常
方式一:在执行任务的Runnable的run方法中添加try…catch来手动捕获异常
方式二:继承ThreadPoolExecutor来重写afterExecute方法,处理异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30class MyThreadPoolExecutor extends ThreadPoolExecutor {
public MyThreadPoolExecutor(int corePoolSize) {
this(corePoolSize,corePoolSize,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r,t);
if (t == null && r instanceof Future<?>) {
try {
Object result = ((Future<?>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) { // 根据上述分析可知,FutureTask抛出来的异常是ExecutionException,所以捕获该异常,就可以获取到任务中所抛出来的异常
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
// 此时如果t不为null,说明在进行get操作时有异常出现
if(t != null){
System.out.println("afterExecute获取异常"+t.getMessage());
}
}
}
这个afterExecute是在线程池执行任务的过程中调用的
1 | Throwable thrown = null; |