前言
在开发过程中处理异步任务的时候,我们基本都会使用自定义的线程池,进行异步任务的处理,使用自定义的线程池处理任务的好处,无非就是降低资源消耗、提高响应速度,这里就不做过多的解释说明了,主要来探讨下,如果异步任务中出现异常,线程池是继续运行还是停下来报错呢;
线程池提交的方式
线程池提交的两种方式,与一种是submit,一种是execute,通过创建线程池来分别使用两种提交方式,并且在提交的过程中抛出异常,看看会有什么区别;
submit
先用submit提交,返回Future对象,然后通过get方法获取到我们自己抛出的异常;
java
private final static ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 0l, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(1000));
public static void main(String[] args) throws InterruptedException, ExecutionException {
Future<Object> feature = (Future<Object>) executor.submit(new Runnable() {
@Override
public void run() {
System.out.println("submit 方式提交异常");
throw new RuntimeException("submit提交方式执行异常");
}
});
feature.get();
}
执行结果可能看到,当前线程不会中止,我们通过返回的Future对象,并且调用了get方法获取到了存在RunnableFuture里面的异常信息,但是整个线程池还在继续运行,并没有停下了;

execute
再用execute方法提交;
csharp
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println("execute方式提交异常");
throw new RuntimeException("execute提交方式执行异常");
}
});
执行结果可以看到,当前线程会中止,控制台自己抛出了异常,线程池依旧还是在运行,没有听停止掉;

总体执行结果
线程池出现异常后,并不会让整个线程池停下来,不同的是submit的执行如果出现异常当前线程不会被中止,而execute执行出现异常会让当前线程中止并且抛出异常;
线程池执行源码分析
Submit方法的实现还是调用了execute方法,将RunnableFuture对象提交给了 ThreadPoolExecutor;

Execute方法,有个判断如果当前活跃线程小于核心线程,就会调用addWorker创建线程Worker的核心方法,这个方法的具体细节就不做过多的解释了,有兴趣的小伙伴可以自己去看下源码;
接下来addWorker方法里面有个start方法;

ThreadPoolExecutor里面有个Worker内部类,这个Worker内部类主要是进行工作线程的创建,通过ThreadFactory创建,这个this就是指的上面addWorker方法里面的"t";

执行Exceute方法断点
Worker类的runWorker方法执行工作线程,我们在task.run方法这边打一个断点,我们执行上面的execute方法看看 ,可以看到外面提交的异常在这里得到了捕获,在catch重新抛出了异常,这里进行了捕获意味了worker线程被中止;

执行Submit方法断点
再次回到submit里面的newTaskFor方法,这个方法返回了FutureTask对象,FutureTask是实现了RunnableFuture接口所以也会执行run方法;

执行submit方法后发现,在catch里面并没有将这个异常进行抛出,而是调用了setException方法,Callable对象的call方法被try/catch包住了;

setException方法用outcome接收了异常t,

FutureTask的get方法,调用了report方法;

所以只有调用FutureTask.get()的时候才能获取到异常信息;

Demo分析
上面关于线程池提交任务的demo,我们在提交的时候执行了try/catch代码块,这种写法非常繁琐,我们其实可以使用ThreadFactory,自定义线程工厂,在自定义的线程工厂里面统一管理线程池的异常处里;
java
public class CustomThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix ="pool-" +
poolNumber.getAndIncrement() +
"-thread-";
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement()) {
@Override
public void run() {
try {
super.run();
} catch (Exception e) {
System.out.println("异常信息:" + e.getMessage());
}
}
};
return t;
}
}
线程池定义加入自定义CustomThreadFactory

执行结果 统一异常处理;

总结
只有了解了线程池提交任务的步骤,以及异常的处理,才能让我们在使用线程池的时候出现异常,能够及时处理,避免线程池提交任务处理失败导致数据丢失;