开发者社区 > 博文 > "线程池中线程异常后:销毁还是复用?"
分享
  • 打开微信扫码分享

  • 点击前往QQ分享

  • 点击前往微博分享

  • 点击复制链接

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

  • jd****
  • 2024-03-13
  • IP归属:北京
  • 108浏览

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

           需要说明,本文的线程池都是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()方法时,可以捕获到异常,不会把这个线程移除掉,也不会创建新的线程放入到线程池中。

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

    文章数
    1
    阅读量
    8

    作者其他文章