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

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

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

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

点赞
收藏

评论区

加载中...

相关推荐

java 面试知识点笔记(十三)多线程与并发

java线程池,利用Exceutors创建不同的线程池满足不同场景需求:1.newSingleThreadExecutor() 创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。2.

java_线程池

血一样的教训,今天上午参加了一家现场面试java。在这之前,我一直认为我的java基础还是可以的,而今天一问三不知。现在将面试的问题整理出来一、说说java中的线程池?  1.线程池:线程池是线程的集合,不用自己创建线程,把线程直接给线程池,由线程池处理。   2.过程:首先,使用线程池可以重复利用已有的线程继续执行任务,避免线程在

springboot+线程池使用

关于线程和线程池的学习,我们可以从以下几个方面入手:第一,什么是线程,线程和进程的区别是什么第二,线程中的基本概念,线程的生命周期第三,单线程和多线程第四,线程池的原理解析第五,常见的几种线程池的特点以及各自的应用场景一、什么是线程线程,程序执

java四大线程池

一、为什么需要使用线程池  1、减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。2、可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。Java中创建和销毁一个线程是比较

Java多线程之线程池7大参数、底层工作原理、拒绝策略详解

Java多线程之线程池7大参数详解目录企业面试题线程池7大参数源码线程池7大参数详解底层工作原理详解线程池的4种拒绝策略理论简介面试的坑:线程池实际中使用哪一个?1\.企业面试题线程池的工作原理,几个重要参数,然后给了具体几个参数分析线程池会怎么做,最后问阻塞队列用是什么?线程池的构造类的方

Java 线程池原理分析

1.简介线程池可以简单看做是一组线程的集合,通过使用线程池,我们可以方便的复用线程,避免了频繁创建和销毁线程所带来的开销。在应用上,线程池可应用在后端相关服务中。比如Web服务器,数据库服务器等。以Web服务器为例,假如Web服务器会收到大量短时的HTTP请求,如果此时我们简单的为每个HTTP请求创建一个处理线程,那么服务器