如何关闭线程池 为什么关闭线程池线程池关闭的意义不仅仅在于结束线程执行避免内存溢出因为大多使用的场景并非上述示例那样朝生夕死。线程池一般是持续工作的全局场景如数据库连接池。线程池大家都知道用来实现资源复用的。避免了线程频繁的创建和销毁的开销。待到使用完毕后都要关闭线程池以释放资源避免线程全部泄漏白白的浪费了内存。容易导致内存吃紧的时候造成死机。线程池有运行任务自然也有关闭任务线程池手动关闭无非是两个方法其实无非就是两个方法 shutdown()/shutdownNow()。自动关闭则需要注意线程池配置技巧。线程池手动关闭关闭方法线程池有两个方法 shutdown()/shutdownNow()用来关闭二者有着重要的区别shutdown() 执行后停止接受新任务会把队列的任务执行完毕。shutdownNow() 也是停止接受新任务但会中断所有的任务将线程池状态变为 stop。下段源码是shutdown() 和shutdownNow()从中可以看到两个方法的不同/** * Initiates an orderly shutdown in which previously submitted * tasks are executed, but no new tasks will be accepted. * Invocation has no additional effect if already shut down. * * pThis method does not wait for previously submitted tasks to * complete execution. Use {link #awaitTermination awaitTermination} * to do that. * * throws SecurityException {inheritDoc} */ public void shutdown() { final ReentrantLock mainLock this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(SHUTDOWN); interruptIdleWorkers(); onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } tryTerminate(); } /** * Attempts to stop all actively executing tasks, halts the * processing of waiting tasks, and returns a list of the tasks * that were awaiting execution. These tasks are drained (removed) * from the task queue upon return from this method. * * pThis method does not wait for actively executing tasks to * terminate. Use {link #awaitTermination awaitTermination} to * do that. * * pThere are no guarantees beyond best-effort attempts to stop * processing actively executing tasks. This implementation * cancels tasks via {link Thread#interrupt}, so any task that * fails to respond to interrupts may never terminate. * * throws SecurityException {inheritDoc} */ public ListRunnable shutdownNow() { ListRunnable tasks; final ReentrantLock mainLock this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(STOP); interruptWorkers(); tasks drainQueue(); } finally { mainLock.unlock(); } tryTerminate(); return tasks; }shutdown()将线程池的状态修改为SHUTDOWN状态然后调用interruptIdleWorkers方法来中断空闲的线程。方法是安全的关闭线程池调用shutdown方法后不是立即关闭线程池而是在线程池中执行很多任务或者等待队列中执行任务等待所有任务完成后关闭线程池shutdownNow() 方法的执行逻辑将线程池状态修改为STOP然后调用线程池里的所有线程的interrupt方法。shutdownNow()立即关闭线程池首先向线程池中的线程发送中断信号尝试中断线程然后将等待队列的任务返回调用人员让调用人员补救这些任务。通常可以根据自己的业务需求选择合适的方法停止线程池。例如通常可以用shutdown()的方法关闭完成提交的任务但如果情况紧急可以用shutdownnow方法加快线程池的结束速度。我通常是按照以下方式关闭线程池的/** * author Evan Walker * version 1.0 * desc 线程池关闭demo */ public class ThreadPoolExecutorDemo { private final static Logger logger LoggerFactory.getLogger(ThreadPoolExecutorDemo.class); //起的线程数 private static final Integer THREAD_NUM 5; //创建线程池 ThreadPoolExecutor pool new ThreadPoolExecutor(THREAD_NUM, THREAD_NUM * 2, 0, TimeUnit.SECONDS, new LinkedBlockingQueue(100)); public void ThreadPoolExecutorClose(Runnable command){ long start System.currentTimeMillis(); for (int i0; i 5; i) { pool.execute(command); } pool.shutdown(); try { while (!pool.awaitTermination(1, TimeUnit.SECONDS)) { logger.info(线程还在执行。。。); } }catch (InterruptedException e){ logger.error(ThreadPoolExecutor InterruptedException ,e); } long end System.currentTimeMillis(); logger.info(一共处理了【{}】, (end - start)); } }也可以设置一个shutdownHook来关闭线程池Runtime.getRuntime().addShutdownHook(new Thread(() - { try { pool.shutdown(); while(!pool.awaitTermination(10,TimeUnit.SECONDS)) {} } catch(Exception e) { logger.error(pool shutdown has exception ,e); } }));pool.awaitTermination(1, TimeUnit.SECONDS) 会每隔一秒钟检查一次是否执行完毕状态为 TERMINATED当从 while 循环退出时就表明线程池已经完全终止了。关闭状态判断1 isShutdownboolean isShutdown()此方法在线程已经开始关闭的时候返回true其他时候返回false即是否调用了线程池的shutdown或shutdownnow方法返回true不代表线程池已经终止了还有可能是需要线程池需要等待正在执行的任务或任务队列中的任务执行完。2 isTerminated此方法返回true表示线程池的所有任务都执行完毕了线程池终结了如果线程池调用了shutdown但是任务还没执行完isShutdown返回true而isTerminated返回false。3.awaittermination方法是判断线程池是否完全关闭与isterminated相似但接受等待时间。调用该方法可能发生以下情况1等待期间(包括进入等待状态)线程池关闭提交的任务(包括执行中和队列中等待的)全部完成相当于线程池结束方法返回true2等待超时后最初的线程池没有发生结束法回到false3等待期间线程中断方法会抛出互联网异常。线程池自动关闭ThreadPoolExecutor类源码中有这样一句话A pool that is no longer referenced in a program and has no remaining threads will be shutdown automatically.没有引用指向且没有剩余线程的线程池将会自动关闭。先来看一下ThreadPoolExecutor参数最全的构造方法/** * param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {code allowCoreThreadTimeOut} is set * 核心线程数即使是空闲状态也可以在线程池存活的线程数量除非 * allowCoreThreadTimeOut 设置为 true。 * param keepAliveTime when the number of threads is greater than * the core, this is the maximum time that excess idle threads * will wait for new tasks before terminating. * 存活时间对于超出核心线程数的线程空闲时间一旦达到存活时间就会被销毁。 */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueRunnable workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { ... ... }方案一自动关闭线程池核心线程数为 0 并指定线程存活时间public class ThreadPoolTest { public static void main(String[] args) { // 重点关注 corePoolSize 和 keepAliveTime其他参数不重要 ThreadPoolExecutor executor new ThreadPoolExecutor(0, 5, 15L, TimeUnit.SECONDS, new LinkedBlockingQueue(15)); for (int i 0; i 20; i) { executor.execute(() - { // 简单地打印当前线程名称 System.out.println(Thread.currentThread().getName()); }); } } }通过执行打印可以发现线程池在15秒之后自动关闭因为它核心线程核心线程数为 0 并指定线程存活时间且后续没有继续使用到该线程池在其最后一个线程执行完毕后15秒之后会优雅的关闭线程池。而如果将corePoolSize设置为大于0的数字再运行以上代码那么线程池将一直处于等待状态而不能关闭因为核心线程不受keepAliveTime控制所以会一直存活程序也将一直不能结束。运行效果如下 (corePoolSize设置为5其他参数不变)线程池工作时当有新的任务到来时程序会先判断线程池当前线程数是否达到corePoolSize(核心线程数)没达到则创建线程执行任务达到则尝试将任务放入任务队列 (workQueue)。如果将corePoolSize设置为0的话新到来的任务会永远优先被放入任务队列然后等待被处理这显然会影响程序的执行效率。所以就有了下面的解决方案。方案二自动关闭线程池通过 allowCoreThreadTimeOut 控制核心线程存活时间从JDK 1.6开始ThreadPoolExecutor类新增了一个allowCoreThreadTimeOut字段/** * If false (default), core threads stay alive even when idle. * If true, core threads use keepAliveTime to time out waiting * for work. * 默认为false核心线程处于空闲状态也可一直存活 * 如果设置为true核心线程的存活状态将受keepAliveTime控制超时将被销毁 */ private volatile boolean allowCoreThreadTimeOut;这个字段值默认为false可使用allowCoreThreadTimeOut()方法对其进行设置如果设置为true那么核心线程数也将受keepAliveTime控制此方法源码如下public void allowCoreThreadTimeOut(boolean value) { // 核心线程存活时间必须大于0一旦开启keepAliveTime 也必须大于0 if (value keepAliveTime 0) throw new IllegalArgumentException(Core threads must have nonzero keep alive times); // 将 allowCoreThreadTimeOut 值设为传入的参数值 if (value ! allowCoreThreadTimeOut) { allowCoreThreadTimeOut value; // 开启后清理所有的超时空闲线程包括核心线程 if (value) interruptIdleWorkers(); } }public class ThreadPoolTest { public static void main(String[] args) { // 这里把corePoolSize设为5keepAliveTime保持不变 ThreadPoolExecutor executor new ThreadPoolExecutor(5, 5, 15L, TimeUnit.SECONDS, new LinkedBlockingQueue(15)); // 允许核心线程超时销毁 executor.allowCoreThreadTimeOut(true); for (int i 0; i 20; i) { executor.execute(() - { System.out.println(Thread.currentThread().getName()); }); } } }运行结果可以看到程序在打印结束后等待了15s然后自行退出说明线程池已自动关闭也就是allowCoreThreadTimeOut()方法发挥了作用。这样我们就实现了可自动关闭且核心线程数不为0的线程池。方案三自动关闭线程池线程池中的线程设置为守护线程public class ThreadPoolTest { public static void main(String[] args) { ThreadPoolExecutor executor new ThreadPoolExecutor(3, 5, 15L, TimeUnit.SECONDS, new LinkedBlockingQueue(15),new ThreadFactory() { public Thread newThread(Runnable r) { Thread thread new Thread(r, r.hashCode()); thread.setDaemon(true);//设置成守护线程 return thread; } }); for (int i 0; i 20; i) { executor.execute(() - { // 简单地打印当前线程名称 System.out.println(Thread.currentThread().getName()); }); } } }虽然corePoolSize0而且没有设置allowCoreThreadTimeOut但是在创建线程池时通过ThreadFactory指定了线程为守护线程。当线程打印结束后无需等待程序正常退出说明线程池关闭了。PS守护进程Daemon是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。也就是说守护线程不依赖于终端但是依赖于系统与系统“同生共死”。文章参考https://segmentfault.com/a/1190000021225019java线程池的关闭_线程池 非核心线程关闭的配置-CSDN博客

相关新闻

最新新闻

大模型在线服务延迟优化:从平坦延迟问题到四层排查框架

大模型在线服务延迟优化:从平坦延迟问题到四层排查框架

最近在折腾几个大模型应用项目时,遇到一个让我停下来思考了很久的现象:一个看似简单的文本生成任务,在本地测试时响应飞快,一旦部署到线上,面对真实用户的并发请求,响应时间就变得飘忽不定,甚至…

2026/8/22 18:55:03
Google差分隐私库differential-privacy怎么给统计加噪声

Google差分隐私库differential-privacy怎么给统计加噪声

Google差分隐私库differential-privacy怎么给统计加噪声 【免费下载链接】differential-privacy Googles differential privacy libraries. 项目地址: https://gitcode.com/gh_mirrors/di/differential-privacy 想发布餐厅分时段客流,又要保证任何个人反推不…

2026/8/22 18:55:03
ai免费写论文实用吗?实测3款一键生成论文工具,结果有好有坏!

ai免费写论文实用吗?实测3款一键生成论文工具,结果有好有坏!

宝子们,有没有人跟我一样,一提到写论文就头皮发麻? 熬夜熬到凌晨三点,结果导师一句"逻辑不通"打回重写,谁懂啊! 说真的,我之前也以为 AI 写论文是智商税,直到自己踩了无数…

2026/8/22 18:55:03
ClawMark基准测试:AI智能体在动态办公环境中的协作能力评估

ClawMark基准测试:AI智能体在动态办公环境中的协作能力评估

1. 项目概述:为什么我们需要一个“活”的AI同事评测场?最近和几个做AI Agent的朋友聊天,大家普遍有个感觉:现在的AI智能体评测,有点“纸上谈兵”的意思。我们训练一个Agent,让它去订机票、写周报、分析数据…

2026/8/22 18:55:03
Falcon 180B平民化部署指南:量化与卸载技术降低千亿模型硬件门槛

Falcon 180B平民化部署指南:量化与卸载技术降低千亿模型硬件门槛

1. 从“巨无霸”到“家常菜”:Falcon 180B的硬件需求迷思最近在AI社区里,Falcon 180B这个名字被反复提及,它就像模型界突然冒出来的一个“巨无霸”,参数规模达到了惊人的1800亿。随之而来的,是各种耸人听闻的硬件需求传…

2026/8/22 18:55:03
Android开发工程师核心技能与面试指南

Android开发工程师核心技能与面试指南

1. Android开发工程师的核心技能全景图在移动互联网时代,Android开发工程师始终是技术领域的热门岗位。根据最新的开发者调查报告显示,全球Android活跃设备数量已突破30亿台,而中国市场占比超过40%。这个庞大的数字背后,是对高质量…

2026/8/22 18:50:03