面试官:说说停止线程池的执行流程?

简介: 面试官:说说停止线程池的执行流程?

对于我们使用的线程池 ThreadPoolExecutor 来说,停止线程池的方法有以下两个:

  1. shutdown():优雅的关闭线程池,即不再接受新任务,但会等待已提交任务(包括正在执行的任务和在队列中等待的任务)执行完毕。等待所有任务都执行完毕后,线程池才会进入终止状态
  2. shutdownNow():尝试停止所有正在执行的任务,并返回等待执行的任务列表。正在执行的任务可能会被中断,适用于需要立即停止线程池,但不关心正在执行的任务是否立即完成的情况下。

1.代码演示

下面通过代码案例,咱们来了解一下 shutdown() 和 shutdownNow() 方法的具体使用。

1.1 shutdown() 方法执行

我们将线程池核心和最大线程数都设置为 2,任务队列可以存储 10 个任务,一次性添加了 5 个任务,每个任务执行 2s 以上,添加完任务之后执行停止方法,并在 1s 之后尝试添加另一个新任务,如下代码所示:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorShutdownTest {
   
    public static void main(String[] args) {
   
        // 创建线程
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2,
                2,
                1000,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(10),
                new RejectedExecutionHandler() {
   
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
   
                        System.out.println("执行拒绝策略");
                    }
                });
        // 添加任务
        for (int i = 0; i < 5; i++) {
   
            executor.submit(() -> {
   
                String tName = Thread.currentThread().getName();
                System.out.println(tName + ":开始执行任务!");
                try {
   
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
                System.out.println(tName + ":结束执行任务!");
            });
        }
        // 停止线程
        executor.shutdown();
        try {
   
            Thread.sleep(1000);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
        // 添加新任务
        executor.submit(() -> System.out.println("最后一个新任务"));
    }
}

以上程序的执行结果如下:

从以上结果可以看出,执行 shutdown() 方法后,程序会等待线程池中的所有任务全部执行完在关闭,再次期间线程池会拒绝加入新任务,并调用线程池的拒绝策略

1.2 shutdownNow()方法执行

如果将 shutdown() 方法换成 shutdownNow() 方法后,以上程序的执行结果如下:

也就是说,调用 shutdownNow() 之后,正在执行的任务会被立即停止,且任务队列中未执行的任务也会被清除,调用 shutdownNow() 方法后新加入的任务会被拒绝,并执行线程池的拒绝策略

2.shutdown()执行流程

shutdown() 方法执行源码如下:

public void shutdown() {
   
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
   
        checkShutdownAccess();
        advanceRunState(SHUTDOWN);
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
   
        mainLock.unlock();
    }
    tryTerminate();
}

该源码执行流程如下:

  1. 加锁:在多线程环境下,关闭操作涉及到修改关键状态和执行一些可能影响多个线程的操作。使用锁可以确保这些操作的原子性和一致性,避免多个线程同时进行关闭操作导致数据不一致或出现意外情况
  2. 检查关闭权限:在关闭之前进行状态检查可以确保关闭操作是合法的,避免在不适当的时候进行关闭。推进状态可以让其他代码部分能够根据当前执行器的状态做出正确的反应。
  3. 将状态设置为 SHUTDOWN:阻止新任务提交但完成现有任务。
  4. 中断空闲线程
  5. 调用 onShutdown 方法(钩子方法)可能用于在关闭时执行一些特定的清理或自定义操作,比如释放资源等。
  6. 释放锁
  7. 尝试终止线程池:如果所有任务已完成的情况下,会真正的终止线程池。

shutdown() 方法的执行流程如下图所示:

课后思考

为什么需要关闭线程池?关闭线程池的场景有哪些?说说 shutdownNow() 的执行流程?

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

相关文章
|
1月前
|
运维 测试技术
拆分软件测试流程,一张图秒杀所有面试
本文主要介绍了软件测试流程的核心内容,包括需求分析、测试用例编写、测试执行、缺陷提交及回归测试等关键步骤。以迭代测试为例,详细说明了每个环节的具体操作和注意事项,并提供了一张测试流程图以便理解。测试流程确保了软件质量,是面试中常见的考察点。
47 7
拆分软件测试流程,一张图秒杀所有面试
|
29天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
1月前
|
消息中间件 前端开发 NoSQL
面试官:线程池遇到未处理的异常会崩溃吗?
面试官:线程池遇到未处理的异常会崩溃吗?
62 3
面试官:线程池遇到未处理的异常会崩溃吗?
|
1月前
|
消息中间件 前端开发 NoSQL
面试官:如何实现线程池任务编排?
面试官:如何实现线程池任务编排?
27 1
面试官:如何实现线程池任务编排?
|
17天前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
讲解Activity的启动流程了,Activity的启动流程相对复杂一下,涉及到了Activity中的生命周期方法,涉及到了Android体系的CS模式,涉及到了Android中进程通讯Binder机制等等, 首先介绍一下Activity,这里引用一下Android guide中对Activity的介绍:
30 4
|
17天前
|
Android开发 开发者
Android面试之Activity启动流程简述
每个Android开发者都熟悉的Activity,但你是否了解它的启动流程呢?本文将带你深入了解。启动流程涉及四个关键角色:Launcher进程、SystemServer的AMS、应用程序的ActivityThread及Zygote进程。核心在于AMS与ActivityThread间的通信。文章详细解析了从Launcher启动Activity的过程,包括通过AIDL获取AMS、Zygote进程启动以及ActivityThread与AMS的通信机制。接着介绍了如何创建Application及Activity的具体步骤。整体流程清晰明了,帮助你更深入理解Activity的工作原理。
22 0
|
2月前
|
存储 安全 Java
【多线程面试题十七】、如果不使用synchronized和Lock,如何保证线程安全?
这篇文章探讨了在不使用`synchronized`和`Lock`的情况下保证线程安全的方法,包括使用`volatile`关键字、原子变量、线程本地存储(`ThreadLocal`)以及设计不可变对象。
【多线程面试题十二】、阻塞线程的方式有哪些?
线程阻塞的方式包括调用sleep()方法、阻塞式IO操作、等待同步监视器的获取、等待通知(notify),以及慎用的suspend()方法。
|
2月前
|
存储 安全 Java
【多线程面试题 七】、 说一说Java多线程之间的通信方式
Java多线程之间的通信方式主要有:使用Object类的wait()、notify()、notifyAll()方法进行线程间协调;使用Lock接口的Condition的await()、signal()、signalAll()方法实现更灵活的线程间协作;以及使用BlockingQueue作为线程安全的队列来实现生产者和消费者模型的线程通信。
|
2月前
|
安全 Java
【多线程面试题 六】、 如何实现线程同步?
实现线程同步的方法包括同步方法、同步代码块、使用ReentrantLock、volatile关键字以及原子变量类,以确保线程安全和数据一致性。