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

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

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

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

代码验证

验证execute提交线程池中

测试代码:

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);
        }
    }
}

执行结果如下:

图片图片

结论:

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

验证submit提交线程池中

测试代码:

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);
        }
    }
}

执行结果如下:

图片图片

结论:

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

源码解析

1.java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable);

图片图片

2. 查看execute方法的执行逻辑;

图片图片

3. java.util.concurrent.ThreadPoolExecutor#processWorkerExit;

图片图片

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

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

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

图片图片

图片图片

图片图片

图片图片

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

总结

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

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

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

责任编辑:武晓燕 来源: 一安未来
相关推荐

2024-04-02 09:53:08

线程池线程堆栈

2024-08-29 08:54:35

2023-02-02 08:56:25

线程池线程submit

2024-10-11 16:57:18

2019-09-26 10:19:27

设计电脑Java

2020-02-26 15:12:43

线程池增长回收

2022-09-29 09:19:04

线程池并发线程

2023-10-26 08:25:35

Java线程周期

2021-06-17 06:57:10

SpringBoot线程池设置

2011-06-01 11:23:09

Android 线程

2021-06-11 11:28:22

多线程fork单线程

2010-02-24 11:19:00

Python主线程

2024-11-06 12:59:42

多线程销毁线程切换

2012-02-21 14:14:47

Java

2012-01-16 09:00:56

线程

2024-04-08 10:09:37

TTLJava框架

2022-06-24 06:43:57

线程池线程复用

2009-08-12 13:22:44

Singleton模式

2022-09-06 08:25:13

线程异步任务

2022-10-12 09:01:52

Linux内核线程
点赞
收藏

51CTO技术栈公众号