写点什么

"线程池中线程异常后:销毁还是复用?"

  • 2024-03-28
    北京
  • 本文字数:2299 字

    阅读完需:约 8 分钟

一、一个线程池中的线程异常了,那么线程池会怎么处理这个线程?

需要说明,本文的线程池都是 java.util.concurrent.ExecutorService 线程池,本文将围绕验证,阅读源码俩方面来解析这个问题。

二、代码验证

2.1 验证 execute 提交线程池中

2.1.1 测试代码:

   public class ThreadPoolExecutorDeadTest {
public static void main(String[] args) throws InterruptedException { ExecutorService executorService = buildThreadPoolExecutor(); executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute-exception")); executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute"));

Thread.sleep(5000); System.out.println("再次执行任务=======================");
executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute")); }

public static ExecutorService buildThreadPoolExecutor() { return new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), new ThreadFactoryBuilder().setNameFormat("test-%s").build() , new ThreadPoolExecutor.CallerRunsPolicy()); }
private static void exeTask(String name) { String printStr = "[thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name + "]"; if ("execute-exception".equals(name)) { throw new RuntimeException(printStr + ", 我抛异常了"); } else { System.out.println(printStr); } }}
复制代码

2.1.2 执行结果如下:

2.1.3 结论:

execute 提交到线程池的方式,如果执行中抛出异常,并且没有在执行逻辑中 catch,那么会抛出异常,并且移除抛出异常的线程,创建新的线程放入到线程池中。

2.2 验证 submit 提交线程池中

2.2.1 测试代码:

public class ThreadPoolExecutorDeadTest {
public static void main(String[] args) throws InterruptedException { ExecutorService executorService = buildThreadPoolExecutor(); executorService.submit(() -> exeTask("execute")); executorService.submit(() -> exeTask("execute")); executorService.submit(() -> exeTask("execute-exception")); executorService.submit(() -> exeTask("execute")); executorService.submit(() -> exeTask("execute"));

Thread.sleep(5000); System.out.println("再次执行任务=======================");
executorService.submit(() -> exeTask("execute")); executorService.submit(() -> exeTask("execute")); executorService.submit(() -> exeTask("execute")); executorService.submit(() -> exeTask("execute")); executorService.submit(() -> exeTask("execute")); }

public static ExecutorService buildThreadPoolExecutor() { return new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), new ThreadFactoryBuilder().setNameFormat("test-%s").build() , new ThreadPoolExecutor.CallerRunsPolicy()); }
private static void exeTask(String name) { String printStr = "[thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name + "]"; if ("execute-exception".equals(name)) { throw new RuntimeException(printStr + ", 我抛异常了"); } else { System.out.println(printStr); } }}
复制代码

2.2.2 执行结果如下:

2.2.3 结论:

submit 提交到线程池的方式,如果执行中抛出异常,并且没有 catch,不会抛出异常,不会创建新的线程。

三、源码解析

3.1 java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable)

3.2 查看 execute 方法的执行逻辑 java.util.concurrent.ThreadPoolExecutor#runWorker

3.3 java.util.concurrent.ThreadPoolExecutor#processWorkerExit

可以发现,如果抛出异常,会移除抛出异常的线程,创建新的线程。

3.4 为什么 submit 方法,没有创建新的线程,而是继续复用原线程?

还记得,我们在 3.1 的时候,发现 submit 也是调用了 execute 方法,但是在调用之前,包装了一层 RunnableFuture,那一定是在 RunnableFuture 的实现 FutureTask 中有特殊处理了,我们查看源码可以发现。






但是,我们通过 java.util.concurrent.FutureTask#get(),就可以获取对应的异常信息。

四、总结

当一个线程池里面的线程异常后:

◦当执行方式是 execute 时,可以看到堆栈异常的输出,线程池会把这个线程移除掉,并创建一个新的线程放到线程池中。


◦当执行方式是 submit 时,堆栈异常没有输出。但是调用 Future.get()方法时,可以捕获到异常,不会把这个线程移除掉,也不会创建新的线程放入到线程池中。


以上俩种执行方式,都不会影响线程池里面其他线程的正常执行。

发布于: 刚刚阅读数: 5
用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
"线程池中线程异常后:销毁还是复用?"_京东科技开发者_InfoQ写作社区