关闭线程池:shutdown()方法与isTerminated()和awaitTermination()配合使用

本文介绍了两种关闭Java线程池的方法:使用shutdown()结合isTerminated()检查线程池状态,以及使用shutdown()结合awaitTermination()等待线程池完成所有任务。通过示例代码展示了如何确保线程池正确关闭。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天在项目中使用了线程池,发现不会把线程关闭掉,所以就看了这方面,并作个记录。

1、shutdown()和isTerminated()配合使用

目前项目中使用的shutdown()和isTerminated()配合使用。
shutdown()方法会关闭线程池,这个方法会等待正在运行和队列里的任务都执行完毕后,才会关闭线程池。
isTerminated()每次调用此方法都会返回一个boolean值,true表示线程已关闭,false表示线程池还在运行。此方法必须放在shutdown()方法之后,否则返回值一定为false。

System.out.println("开始");
ExecutorService executors = Executors.newFixedThreadPool(10);
for (int i = 0; i < 2; i++) {
    executors.execute(new Runnable() {
        @Override
        public void run() {
            for (int j = 0; j < 3; j++) {
                try {
                    System.out.println("正在运行的线程"+Thread.currentThread().getId()
                    	+Thread.currentThread().getName()+j);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });
}
System.out.println("执行shutdown");
executors.shutdown();
while (true) {
    if (executors.isTerminated()) {
        System.out.println("所有的子线程都结束了!");
        break;
    }
}
System.out.println("执行完毕");

运行结果

开始
执行shutdown
正在运行的线程12pool-1-thread-10
正在运行的线程13pool-1-thread-20
正在运行的线程13pool-1-thread-21
正在运行的线程12pool-1-thread-11
正在运行的线程13pool-1-thread-22
正在运行的线程12pool-1-thread-12
所有的子线程都结束了!
执行完毕

2、shutdown()和awaitTermination()配合使用

awaitTermination()方法,有两个参数,第一个是long类型的数值,第二个是时间类型TimeUnit,用于设置阻塞时间。
awaitTermination()方法是一个阻塞的方法,若线程池一直运行则会一直阻塞,直到线程池关闭返回true,或阻塞时间超过你设置的这个时间,则返回false。
此方法必须放在shutdown()方法之后,否则一直在阻塞,或超过设置的阻塞时间返回false。
可以有两种写法:
1、设置一个小的阻塞时间,循环调用awaitTermination()方法,来判断线程是否执行完毕。

System.out.println("开始");
ExecutorService executors = Executors.newFixedThreadPool(10);
for (int i = 0; i < 2; i++) {
    executors.execute(new Runnable() {
        @Override
        public void run() {
            for (int j = 0; j < 3; j++) {
                try {
                    System.out.println("正在运行的线程"+Thread.currentThread().getId()
                    	+Thread.currentThread().getName()+j);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });
}
System.out.println("执行shutdown");
executors.shutdown();
while (!executors.awaitTermination(1, TimeUnit.SECONDS)){
    System.out.println("执行awaitTermination");
}
System.out.println("执行完毕");

运行结果:

开始
执行shutdown
正在运行的线程12pool-1-thread-10
正在运行的线程13pool-1-thread-20
正在运行的线程13pool-1-thread-21
正在运行的线程12pool-1-thread-11
执行awaitTermination
正在运行的线程13pool-1-thread-22
正在运行的线程12pool-1-thread-12
执行awaitTermination
执行完毕

2、设置一个超大的阻塞时间,一直阻塞,直到线程池执行完毕

System.out.println("开始");
ExecutorService executors = Executors.newFixedThreadPool(10);
for (int i = 0; i < 2; i++) {
    executors.execute(new Runnable() {
        @Override
        public void run() {
            for (int j = 0; j < 3; j++) {
                try {
                    System.out.println("正在运行的线程"+Thread.currentThread().getId()
                            +Thread.currentThread().getName()+j);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });
}
System.out.println("执行shutdown");
executors.shutdown();
System.out.println("执行awaitTermination");
executors.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
System.out.println("执行完毕");

运行结果:

开始
执行shutdown
正在运行的线程12pool-1-thread-10
正在运行的线程13pool-1-thread-20
执行awaitTermination
正在运行的线程12pool-1-thread-11
正在运行的线程13pool-1-thread-21
正在运行的线程12pool-1-thread-12
正在运行的线程13pool-1-thread-22
执行完毕

这样awaitTermination()方法只会返回一次,代码中就不需要写while循环。

注意:isTerminated()和awaitTermination()方法必须放在shutdown()方法之后!

<think>我们正在讨论如何正确关闭Java线程池。根据用户的问题,用户想知道如何正确关闭线程池,特别是关于`shutdown()`方法使用。 根据引用[1][2],关闭线程池有两种主要方法:`shutdown()``shutdownNow()`。 - `shutdown()`方法线程池状态置为SHUTDOWN,此时线程池不再接受新任务,但会等待已提交的任务(包括正在执行的队列中等待的)执行完成。 - `shutdownNow()`方法线程池状态置为STOP,尝试中断所有工作线程(包括正在执行任务的线程),并返回等待执行的任务列表。 引用[1]提到,优雅关闭线程池通常使用`shutdown()`方法,因为它允许正在执行的任务完成,同时不再接受新任务。但是,仅仅调用`shutdown()`可能还不够,因为如果任务执行时间很长,我们可能需要等待一段时间。因此,通常配合`awaitTermination()`方法使用,该方法可以设置一个超时时间,等待线程池中的任务执行完毕。 引用[3][4]的示例代码中,在调用`shutdown()`后,程序可能会立即退出,而不管任务是否执行完毕。为了确保所有任务完成,我们可以使用`awaitTermination()`来等待。 因此,正确关闭线程池的步骤通常如下: 1. 调用线程池的`shutdown()`方法,表示不再接受新任务,并开始关闭过程。 2. 调用`awaitTermination(long timeout, TimeUnit unit)`方法,等待一段时间,让线程池中的任务执行完成。如果超时后任务还没完成,可以根据情况选择是否继续等待或采取其他措施(如强制关闭)。 3. 如果希望更及时地响应中断(比如在JVM关闭关闭线程池),可以注册一个JVM钩子(shutdown hook),在钩子中执行上述关闭步骤。 引用[1]还提到,单纯使用`shutdown()`不靠谱,需要结合`awaitTermination()`JVM钩子。 下面是一个示例代码,展示如何优雅关闭线程池: ```java ExecutorService executor = Executors.newFixedThreadPool(10); // ... 提交任务到executor // 开始关闭线程池 executor.shutdown(); // 不再接受新任务 try { // 等待60秒,让现有任务完成 if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { // 如果超时,可以尝试强制关闭使用shutdownNow) executor.shutdownNow(); // 再次等待一段时间,以便响应中断 if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { System.err.println("线程池未完全关闭"); } } } catch (InterruptedException ie) { // 如果等待过程中线程被中断,则再次尝试关闭 executor.shutdownNow(); // 保留中断状态 Thread.currentThread().interrupt(); } ``` 另外,为了确保在JVM关闭时能够关闭线程池,可以添加一个关闭钩子: ```java Runtime.getRuntime().addShutdownHook(new Thread(() -> { // 关闭线程池的逻辑 executor.shutdown(); try { if (!executor.awaitTermination(10, TimeUnit.SECONDS)) { executor.shutdownNow(); } } catch (InterruptedException e) { executor.shutdownNow(); } })); ``` 总结: 优雅关闭线程池的关键是: 1. 使用`shutdown()`启动有序关闭。 2. 使用`awaitTermination()`等待任务完成。 3. 必要时使用`shutdownNow()`进行强制关闭。 4. 考虑使用JVM钩子确保应用退出时关闭线程池。 注意:`shutdownNow()`会尝试中断所有正在执行的任务,因此任务需要能够响应中断(即检查中断状态并提前结束),否则可能无法中断。 因此,在编写任务时,应该确保任务能够响应中断,这样在调用`shutdownNow()`时才能及时停止。 引用[1][2]都强调了`shutdown()``shutdownNow()`的区别,以及`awaitTermination()`的重要性。引用[3][4]则提供了代码示例。 根据以上分析,回答用户的问题:正确关闭线程池方法使用`shutdown()`启动关闭,然后使用`awaitTermination()`等待任务完成,必要时配合`shutdownNow()`JVM钩子。 生成相关问题: 1. 线程池的`shutdown()``shutdownNow()`方法有什么区别? 2. 如何确保线程池中的所有任务都执行完毕后再关闭? 3. 为什么在任务中需要响应中断? 4. 在什么情况下应该使用`shutdownNow()`方法? 5. 如何为线程池设置关闭钩子(shutdown hook)?</think>### Java线程池的正确关闭方法 在Java中,正确关闭线程池需要结合`shutdown()`、`awaitTermination()`异常处理机制,以下是详细步骤: #### 1. **核心关闭流程** ```java ExecutorService es = Executors.newFixedThreadPool(10); // 1. 启动有序关闭 es.shutdown(); // 不再接受新任务,等待已提交任务完成[^1][^2] try { // 2. 设置等待超时时间(推荐30~60秒) if (!es.awaitTermination(60, TimeUnit.SECONDS)) { // 3. 强制终止未完成任务 List<Runnable> unfinished = es.shutdownNow(); System.err.println("强制终止" + unfinished.size() + "个任务"); // 4. 二次等待确认 if (!es.awaitTermination(30, TimeUnit.SECONDS)) { System.err.println("线程池仍未完全终止"); } } } catch (InterruptedException e) { // 5. 处理中断异常 es.shutdownNow(); // 再次尝试终止 Thread.currentThread().interrupt(); // 恢复中断状态 } ``` #### 2. **关键方法对比** | 方法 | 行为 | 适用场景 | |---------------------|----------------------------------------------------------------------|------------------------------| | `shutdown()` | 停止接收新任务,等待已提交任务完成 | 优雅关闭首选[^1][^2] | | `shutdownNow()` | 尝试停止所有任务(包括运行中的),返回未执行任务列表 | 紧急关闭或超时处理 | | `awaitTermination()`| 阻塞等待线程池终止,需设置超时时间 | 必须配合前两者使用[^1][^3] | #### 3. **最佳实践** 1. **任务设计要求**: - 任务代码需检查中断状态:`Thread.currentThread().isInterrupted()` - 阻塞操作需响应`InterruptedException`(如`Thread.sleep()`) 2. **JVM钩子集成**(防止非正常退出): ```java Runtime.getRuntime().addShutdownHook(new Thread(() -> { if (!es.isShutdown()) { es.shutdown(); try { es.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException ignored) {} } })); ``` 3. **状态检查工具**: ```java boolean isTerminated = es.isTerminated(); // 所有任务是否完成[^3] boolean isShutdown = es.isShutdown(); // 是否已开始关闭 ``` #### 4. **为什么需要组合使用?** - 单纯`shutdown()`:无法控制等待时间,可能永久阻塞[^3] - 单纯`shutdownNow()`:暴力中断可能导致数据不一致 - `awaitTermination()`:提供可控等待窗口,平衡优雅关闭系统资源释放 > **关键原则**:优先允许任务完成(`shutdown()`),超时后强制终止(`shutdownNow()`),并通过双重等待确保可靠性[^1][^2]。 --- ### 相关问题 1. **`shutdown()` `shutdownNow()` 在中断线程时有何本质区别?** 2. 如果线程池中有任务永久阻塞(如死循环),如何避免`awaitTermination()`无限等待? 3. 为什么需要在`catch (InterruptedException)`中再次调用`shutdownNow()`? 4. 如何监控线程池关闭过程中的任务完成状态? 5. 除了固定等待时间,还有哪些策略可以动态调整线程池关闭的超时阈值? [^1]: 【Java】如何优雅的关闭线程池 [^2]: 关闭Java线程池的正确方法 [^3]: Java并发编程系列32:线程池shutdown()exs.isTerminated()结合使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值