hi,我是阿昌,今天分享一下如果线程池中线程异常后:销毁还是复用?
下面讨论的线程池是:java.util.concurrent.ExecutorService线程池
通过 execute提交任务
测试代码:
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 30 31 32 33 34 35 36 37
| public static void main(String[] args) { ExecutorService executorService = buildThreadPoolExecutor(); executorService.execute(() -> exeTask("achang-execute")); executorService.execute(() -> exeTask("achang-execute")); executorService.execute(() -> exeTask("achang-execute-exception")); executorService.execute(() -> exeTask("achang-execute")); executorService.execute(() -> exeTask("achang-execute"));
try { Thread.sleep(5000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("再次执行任务=======================");
executorService.execute(() -> exeTask("achang-execute")); executorService.execute(() -> exeTask("achang-execute")); executorService.execute(() -> exeTask("achang-execute")); executorService.execute(() -> exeTask("achang-execute")); executorService.execute(() -> exeTask("achang-execute")); }
public static ExecutorService buildThreadPoolExecutor() { return new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), new ThreadFactoryBuilder().setNameFormat("achang-test-%s").build() , new ThreadPoolExecutor.CallerRunsPolicy()); }
private static void exeTask(String name) { String printStr = "[thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name + "]"; if ("achang-execute-exception".equals(name)) { throw new RuntimeException(printStr + ", 我出现了异常了"); } else { System.out.println(printStr); } }
|
执行结果:
![image]()
结论:
execute 提交到线程池的方式,如果执行中抛出异常,并且没有在执行逻辑中catch,那么会抛出异常,并且移除抛出异常的线程,创建新的线程放入到线程池中。
通过submit提交任务
测试代码:
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 30 31 32 33 34 35 36 37
| public static void main(String[] args) { ExecutorService executorService = buildThreadPoolExecutor(); executorService.submit(() -> exeTask("achang-submit")); executorService.submit(() -> exeTask("achang-submit")); executorService.submit(() -> exeTask("achang-submit-exception")); executorService.submit(() -> exeTask("achang-submit")); executorService.submit(() -> exeTask("achang-submit")); try { Thread.sleep(5000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("再次执行任务======================="); executorService.submit(() -> exeTask("achang-submit")); executorService.submit(() -> exeTask("achang-submit")); executorService.submit(() -> exeTask("achang-submit")); executorService.submit(() -> exeTask("achang-submit")); executorService.submit(() -> exeTask("achang-submit")); } public static ExecutorService buildThreadPoolExecutor() { return new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), new ThreadFactoryBuilder().setNameFormat("achang-test-%s").build() , new ThreadPoolExecutor.CallerRunsPolicy()); } private static void exeTask(String name) { String printStr = "[thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name + "]"; if ("achang-execute-exception".equals(name)) { throw new RuntimeException(printStr + ", 我出现了异常了"); } else { System.out.println(printStr); } }
|
执行结果:
![image]()
结论:
submit 提交到线程池的方式,如果执行中抛出异常,并且没有catch,不会抛出异常,不会创建新的线程。
源码解析
**java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable)**:
![image]()
上面的execute会调用到java.util.concurrent.ThreadPoolExecutor#runWorker:
![image]()
java.util.concurrent.ThreadPoolExecutor#processWorkerExit:
![image]()
上面可以看出,如果抛出异常,会移除抛出异常的线程,创建新的线程。
那为啥submit()方法,没有创建新的线程,而是继续复用原线程?
看源码可以知道submit也是调用了execute方法,但是在调用之前,包装了一层 RunnableFuture,那一定是在RunnableFuture的实现 FutureTask中有特殊处理了,如下查看源码:
java.util.concurrent.FutureTask#run:
![image]()
java.util.concurrent.FutureTask#setException:
![image]()
**java.util.concurrent.FutureTask#get()**:
获取执行结果会调用report()
![image]()
java.util.concurrent.FutureTask#report:
发现通过s状态位来判断任务状态,来判断是否抛出异常
![image]()
上面看到通过java.util.concurrent.FutureTask#get(),就可以获取对应的异常信息。
总结
若当线程池里的线程出现了异常后:
- 如果调用方式是
execute时,可以看到堆栈异常的输出,线程池会把这个线程移除掉,并创建一个新的线程放到线程池中。
- 如果调用是
submit时,堆栈异常没有输出。但是调用Future.get()方法时,可以捕获到异常,不会把这个线程移除掉,也不会创建新的线程放入到线程池中。
上面2种调用方式,都不会影响到线程池里面其他线程的正常执行。