线程池

线程池 #

基础知识 #

  1. InterruptedException是checked异常,必须处理。
  2. 线程池的工作线程会捕获InterruptedException,并根据线程池状态决定是否继续获取任务或终止。并且其会将线程中断状态清除,因此如果需要后续检测中断状态,需要手动重新设置中断标志
catch(InterruptedException e) {
    // 检测到中断,线程池可以选择退出或继续
    Thread.currentThread().interrupt(); // 重新设置中断标志
}

如果在一个函数中已经捕获(catch)了 InterruptedException,那么该异常不会再向上传递,上层函数就不需要再 try-catch 这个异常了。

但需要注意两点:

  1. 如果 catch 后不做任何处理,线程的中断标志会被清除(即 Thread.interrupted() 变为 false),上层检测不到中断状态。
  2. 如果 catch 后希望上层还能检测到中断状态,应在 catch 块中调用 Thread.currentThread().interrupt(); 重新设置中断标志。

常见写法如下:

try {
    // 可能抛出InterruptedException的方法
    Thread.sleep(1000);
} catch (InterruptedException e) {
    // 重新设置中断标志,便于上层检测
    Thread.currentThread().interrupt();
}

这样,上层函数可以通过 Thread.currentThread().isInterrupted() 检测到中断状态,无需再 try-catch InterruptedException

在 catch 块中先恢复中断标志,然后继续将异常抛出,这样既不会丢失中断信号,也能让上层调用者感知并决定如何处理。

推荐写法如下:

try {
    // 可能抛出InterruptedException的方法
    Thread.sleep(1000);
} catch (InterruptedException e) {
    // 重新设置中断标志,便于上层检测
    Thread.currentThread().interrupt();
    throw e; // 继续向上抛出
}

这样既保证了中断信号不丢失,也让上层有机会处理异常。

线程池队列 #

Java 线程池(如 ThreadPoolExecutor)支持多种类型的任务队列(BlockingQueue),不同队列类型会影响线程池的行为和任务调度方式。常见的队列有以下几种:

1. SynchronousQueue #

  • 特点:不存储任何元素,每个插入操作必须等待另一个线程来移除元素,反之亦然。
  • 容量:容量为 0(不存储任务)。
  • 适用场景:任务直接交给线程处理,如果没有空闲线程则尝试创建新线程,直到达到最大线程数。适合任务处理速度快、线程数弹性需求大的场景。
  • 线程池行为:核心线程数为 0 时,适用于 CachedThreadPool,线程数可无限扩展(受最大线程数限制)。

2. LinkedBlockingQueue #

  • 特点:基于链表的有界/无界阻塞队列,FIFO 顺序。
  • 容量:默认 Integer.MAX_VALUE(几乎等于无界),也可指定容量。
  • 适用场景:适合任务量大但线程数有限的场景,任务会被缓存在队列中等待线程处理。
  • 线程池行为:适用于 FixedThreadPool,线程数固定,队列可缓存大量任务,线程数不会超过核心线程数

3. ArrayBlockingQueue #

  • 特点:基于数组的有界阻塞队列,FIFO 顺序。
  • 容量:必须在构造时指定容量,容量固定。
  • 适用场景:适合需要严格限制队列长度的场景,防止任务堆积过多导致内存溢出。
  • 线程池行为:线程数达到核心线程数后,任务进入队列,队列满后才会创建新线程(不超过最大线程数)。

4. PriorityBlockingQueue #

  • 特点:支持优先级排序的无界阻塞队列,元素必须实现 Comparable 接口或提供 Comparator
  • 容量:无界(Integer.MAX_VALUE)。
  • 适用场景:适合需要按优先级处理任务的场景,如定时任务、调度系统等。
  • 线程池行为:线程数达到核心线程数后,任务进入队列,线程数不会超过核心线程数。

5. DelayedWorkQueue(ScheduledThreadPoolExecutor 专用) #

  • 特点:支持延迟和定时任务的无界阻塞队列,元素需实现 Delayed 接口。
  • 容量:无界。
  • 适用场景:适合定时任务、周期性任务调度。
  • 线程池行为:只用于 ScheduledThreadPoolExecutor,不直接用于普通线程池。

队列类型与线程池线程数量关系 #

队列类型线程池线程数增长方式典型线程池
SynchronousQueue任务无法排队,直接创建新线程CachedThreadPool
LinkedBlockingQueue线程数达到核心数后任务排队,不扩容FixedThreadPool
ArrayBlockingQueue线程数达到核心数后任务排队,队列满后扩容自定义线程池
PriorityBlockingQueue线程数达到核心数后任务排队,不扩容自定义线程池
  • SynchronousQueue:最大线程数决定最大并发任务数。
  • LinkedBlockingQueue/PriorityBlockingQueue:核心线程数决定最大并发任务数,队列可缓存大量任务。
  • ArrayBlockingQueue:核心线程数 + 队列容量 + 最大线程数共同决定最大承载能力。

总结
选择合适的队列类型和容量,是设计高效、稳定线程池的关键。实际应用中应根据业务特点、任务量和资源限制合理

线程池停机 #

shutdown 和 shutdownNow 的区别与原理 #

在 Java 的线程池(如 ThreadPoolExecutor)中,关闭线程池主要有两种方法:shutdown()shutdownNow()

1. shutdown() #

  • 作用:启动线程池的有序关闭过程。
  • 行为
    • 不再接受新的任务提交。
    • 已经提交(包括正在执行和队列中等待的)任务会继续执行,直到全部完成。
    • 线程池中的线程在任务执行完毕后会逐步退出。
  • 常用场景:希望线程池优雅关闭,不丢失任何已提交的任务。

2. shutdownNow() #

  • 作用:尝试立即关闭线程池。
  • 行为
    • 不再接受新的任务提交。
    • 会尝试中断所有正在执行的任务(通过调用线程的 interrupt() 方法)。
    • 返回尚未开始执行的任务列表(即队列中还未被取出的任务)。
    • 不能保证所有正在执行的任务都能被成功中断,具体取决于任务代码是否响应中断。
    • 返回值是一个 List,包含所有未开始执行的任务。类型是 List<Runnable>
  • 常用场景:需要尽快停止线程池,且可以接受部分任务未完成或被中断。

shutdownNow() 方法会对线程池中所有正在运行的工作线程调用 interrupt() 方法,尝试中断这些线程。具体来说:

  • shutdownNow() 首先会设置线程池的状态为 STOP,不再接受新任务。
  • 然后会遍历线程池中的所有工作线程,依次调用每个线程的 interrupt() 方法,向线程发送中断信号。
  • 如果线程正在执行会响应中断的阻塞方法(如 sleep()wait()BlockingQueue.take() 等),就会抛出 InterruptedException,线程有机会及时退出。
  • 如果线程没有响应中断(比如在执行普通计算),则需要任务代码自行检测中断状态。

注意interrupt() 只是设置线程的中断标志位,不会强制终止线程,线程必须自己配合检测和响应中断。

3. 代码示例 #

ExecutorService executor = Executors.newFixedThreadPool(2);

// 优雅关闭
executor.shutdown();

// 立即关闭
List<Runnable> notStartedTasks = executor.shutdownNow();

4. 状态变化 #

  • 调用 shutdown()shutdownNow() 后,可以通过 isShutdown() 判断线程池是否处于关闭状态。
  • 通过 isTerminated() 判断线程池是否彻底终止(所有任务都已完成,所有线程都已退出)。

5. 注意事项 #

  • shutdown()shutdownNow() 都不会阻塞主线程。如果需要等待线程池完全关闭,可以调用 awaitTermination() 方法。
  • shutdownNow() 并不能保证所有任务都被中断,只有那些能响应中断的任务才会被终止。

shutdown 和 shutdownNow 的真实案例 #

案例一:使用 shutdown() 优雅关闭线程池 #

场景:批量处理数据,所有任务都需要执行完毕后再关闭线程池。

import java.util.concurrent.*;

public class ShutdownExample {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(2);

        // 提交多个任务
        for (int i = 0; i < 5; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("任务 " + taskId + " 开始执行");
                try {
                    Thread.sleep(2000); // 模拟任务耗时
                } catch (InterruptedException e) {
                    System.out.println("任务 " + taskId + " 被中断");
                }
                System.out.println("任务 " + taskId + " 执行完毕");
            });
        }

        // 优雅关闭线程池
        executor.shutdown();
        System.out.println("已调用 shutdown(),不再接受新任务");

        // 等待线程池完全关闭
        if (executor.awaitTermination(10, TimeUnit.SECONDS)) {
            System.out.println("所有任务执行完毕,线程池关闭");
        } else {
            System.out.println("超时,仍有任务未完成");
        }
    }
}

输出示例:

任务 0 开始执行
任务 1 开始执行
已调用 shutdown(),不再接受新任务
任务 0 执行完毕
任务 2 开始执行
任务 1 执行完毕
任务 3 开始执行
任务 2 执行完毕
任务 4 开始执行
任务 3 执行完毕
任务 4 执行完毕
所有任务执行完毕,线程池关闭

说明
shutdown() 后,线程池不再接受新任务,但会等待所有已提交任务执行完毕,保证任务不丢失,实现优雅关闭。


案例二:使用 shutdownNow() 立即关闭线程池 #

场景:系统检测到紧急情况,需要尽快终止所有任务。

import java.util.concurrent.*;

public class ShutdownNowExample {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(2);

        // 提交多个任务
        for (int i = 0; i < 5; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("任务 " + taskId + " 开始执行");
                try {
                    Thread.sleep(5000); // 模拟长时间任务
                } catch (InterruptedException e) {
                    System.out.println("任务 " + taskId + " 被中断");
                    return;
                }
                System.out.println("任务 " + taskId + " 执行完毕");
            });
        }

        // 等待一会儿后立即关闭线程池
        Thread.sleep(2000);
        System.out.println("调用 shutdownNow(),尝试立即终止所有任务");
        java.util.List<Runnable> notStartedTasks = executor.shutdownNow();

        System.out.println("未开始执行的任务数:" + notStartedTasks.size());

        // 等待线程池关闭
        if (executor.awaitTermination(5, TimeUnit.SECONDS)) {
            System.out.println("线程池已关闭");
        } else {
            System.out.println("线程池关闭超时");
        }
    }
}

输出示例:

任务 0 开始执行
任务 1 开始执行
调用 shutdownNow(),尝试立即终止所有任务
任务 0 被中断
任务 1 被中断
未开始执行的任务数:3
线程池已关闭

说明
shutdownNow() 会尝试中断正在执行的任务(通过 interrupt),并返回还未开始执行的任务列表。只有那些能响应中断的任务才会被及时终止,部分任务可能未被执行。


总结:

  • shutdown() 适用于需要保证所有任务都能执行完毕的场景,关闭过程更平滑。
  • shutdownNow() 适用于紧急停止线程池的场景,可能导致部分任务未完成或被中断,需任务代码能正确响应中断

抛异常InterruptedException #

在 Java 线程池中,线程在执行任务时,只有在调用会响应中断的阻塞方法时才会抛出 InterruptedException。常见的情况包括:

  • 调用 Object 的 wait() 方法时被中断
  • 调用 Thread 的 sleep() 方法时被中断
  • 调用某些阻塞队列的方法(如 BlockingQueue 的 take、put、poll 等)时被中断
  • 调用 CountDownLatch、CyclicBarrier、Semaphore 等同步工具的等待方法时被中断
  • 调用 Future 的 get()、awaitTermination() 等等待方法时被中断

注意:
如果任务代码中没有调用这些会抛出 InterruptedException 的方法,即使线程被中断,也不会抛出该异常。

简而言之,只有在任务代码或线程池内部代码中,线程正处于阻塞等待状态,并且调用了会响应中断的方法时,被中断才会抛出 InterruptedException。

检测InterruptedException #

Java 线程池(如 ThreadPoolExecutor)在执行任务时,会通过工作线程捕获和处理 InterruptedException。下面详细介绍其检测和处理流程:

1. 线程池中的任务执行 #

线程池中的每个工作线程通常会循环从任务队列中获取任务并执行,伪代码如下:

while (true) {
    Runnable task = getTask();
    if (task == null) break;
    try {
        task.run();
    } catch (Exception e) {
        // 处理异常
    }
}

2. InterruptedException 的检测 #

  • 当线程池调用 getTask()(通常是阻塞队列的 take()poll() 方法)时,如果线程被中断,会抛出 InterruptedException
  • 线程池内部会捕获这个异常,并根据线程池的状态决定是否继续获取任务或终止线程。

示例(ThreadPoolExecutor 部分源码简化):

try {
    Runnable r = workQueue.take(); // 可能抛出 InterruptedException
    r.run();
} catch (InterruptedException e) {
    // 检测到中断,线程池可以选择退出或继续
}

3. 线程池如何处理 InterruptedException #

  • 如果线程池正在关闭(shutdown),检测到中断后,线程会退出循环,最终终止。
  • 如果线程池未关闭,通常会忽略中断并继续尝试获取任务,保证线程池的稳定运行。

4. 任务中的 InterruptedException #

  • 如果提交到线程池的任务内部主动检测中断(如在任务代码中调用阻塞方法),也可能抛出 InterruptedException。
  • 线程池不会自动处理任务内部的中断,需任务代码自行捕获和响应。

总结 #

Java 线程池通过捕获工作线程在阻塞等待任务时抛出的 InterruptedException 来检测线程是否被中断,并根据线程池的状态决定线程的生命周期。任务内部的中断需开发者自行处理。

如需源码细节,可参考 ThreadPoolExecutorrunWorker 方法实现。

线程在for循环计算不会自动响应中断 #

如果线程执行的任务只是长时间的 for 循环计算,没有调用任何会抛出 InterruptedException 的阻塞方法(如 sleep、wait、IO 等),那么即使线程池调用了 shutdownNow(),线程只是被设置了中断标志位,但不会自动停止,也不会抛出异常。

只有当任务代码主动检测中断状态(如在循环中定期检查 Thread.currentThread().isInterrupted()),并在检测到中断时主动退出,线程才会及时响应中断。

示例:

while (true) {
    // 计算逻辑
    if (Thread.currentThread().isInterrupted()) {
        // 响应中断,安全退出
        break;
    }
}

总结:

  • 纯计算任务不会自动响应中断,必须在代码中主动检测并处理中断标志,否则线程会一直运行下去。
  • shutdownNow() 只是设置中断标志,不会强制终止线程。

Java 的线程中断机制详解 #

Java 的线程中断是一种协作式的线程终止机制,允许一个线程通过“中断”信号请求另一个线程停止当前操作。中断本身不会强制终止线程,而是由被中断线程自行检测和响应。

1. 中断的基本方法 #

  • Thread.interrupt():向目标线程发送中断信号,将线程的中断标志位设为 true。
  • Thread.isInterrupted():判断线程的中断标志位是否被设置(不会清除标志位)。
  • Thread.interrupted():判断当前线程的中断标志位是否被设置,并将其清除(静态方法)。

2. 中断的实现原理 #

每个线程对象都有一个“中断标志位”。调用 interrupt() 方法时,这个位被设置为 true。线程本身不会因为标志位被设置就自动停止,需要线程自己在合适的位置检测并响应中断

3. 检测和响应中断 #

  • 主动检测:线程可以在执行过程中周期性地调用 isInterrupted()interrupted() 检查中断状态,并决定是否提前结束任务。
  • 阻塞方法响应:如果线程正在调用如 Object.wait()Thread.sleep()BlockingQueue.take() 等会抛出 InterruptedException 的阻塞方法,被中断时会抛出该异常,线程可以在 catch 块中进行清理和退出。

4. 示例代码 #

public class InterruptDemo implements Runnable {
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                // 模拟阻塞操作
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // 捕获到中断异常,进行善后处理
                System.out.println("线程被中断,准备退出");
                Thread.currentThread().interrupt(); // 重新设置中断标志
                break;
            }
            // 其他业务逻辑
        }
        System.out.println("线程安全退出");
    }
}

5. 注意事项 #

  • 中断不是强制终止:线程必须自己配合检测和响应,才能安全退出。
  • 阻塞方法会清除中断标志sleep()wait() 抛出 InterruptedException 时会自动清除中断标志,若需要后续检测,需手动重新设置。
  • 线程池中的线程:线程池会复用线程,任务内部应妥善处理中断,避免影响后续任务。

6. 典型应用场景 #

  • 线程池关闭时中断工作线程
  • 取消耗时任务
  • 响应外部停止信号

总结
Java 的线程中断机制是一种安全、灵活的线程协作终止方式,推荐在多线程编程中优先采用。

Java 底层的中断处理机制原理 #

Java 的线程中断机制底层依赖于每个线程对象内部维护的一个“中断标志位”(interrupted flag)。这个机制主要由 JVM 和操作系统线程调度配合实现,具体原理如下:

1. 中断标志位 #

  • 每个 Java 线程(Thread 实例)在 JVM 层面都有一个 boolean 类型的中断标志位,初始为 false
  • 当调用 thread.interrupt() 方法时,JVM 会将该线程对象的中断标志位置为 true不会直接停止线程

2. 检查与响应 #

  • 线程自身可以通过 isInterrupted()interrupted() 方法检查这个标志位。
  • 某些阻塞方法(如 Object.wait()Thread.sleep()BlockingQueue.take() 等)在检测到线程被中断时,会抛出 InterruptedException,并自动清除中断标志位(即重置为 false)。

3. 阻塞方法的实现 #

  • 阻塞方法内部会周期性检查线程的中断标志位。
  • 如果检测到中断,方法会提前返回并抛出 InterruptedException,让线程有机会进行清理和退出。

4. 操作系统配合 #

  • Java 线程通常映射为操作系统的原生线程(如 Windows 的 native thread 或 Linux 的 pthread)。
  • 但 Java 的中断机制不会直接调用操作系统的线程终止或中断 API,而是完全由 JVM 维护标志位和调度逻辑,保证跨平台一致性。

5. 线程池与中断 #

  • 线程池调用 shutdownNow() 时,会对所有工作线程调用 interrupt(),设置中断标志位。
  • 线程池中的线程如果正处于阻塞状态(如等待任务),会因中断抛出异常并响应停机。

6. 总结 #

  • 中断是一种协作机制,线程本身不会被强制终止,必须在合适位置主动检测和响应中断。
  • JVM 通过维护线程的中断标志位和在阻塞方法中抛出异常,实现了安全、可控的线程中断机制。

参考:

CountDownLatch 详解 #

1. 基本概念 #

CountDownLatch 是 Java 并发包(java.util.concurrent)中的一个同步辅助类,用于让一个或多个线程等待,直到其他线程完成一组操作。它常用于“主线程等待子线程任务完成”或“多线程并发起步”场景。

  • 构造方法CountDownLatch(int count),参数 count 表示计数器的初始值。
  • 核心方法
    • await():调用线程等待,直到计数器为 0。
    • countDown():计数器减 1,当计数器减到 0 时,所有等待的线程被唤醒。

2. await 的用法 #

await() 方法会让当前线程进入等待状态,直到:

  • 计数器变为 0(即所有需要完成的事件都已完成),或
  • 当前线程被中断(此时会抛出 InterruptedException)。

常见用法示例:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        int threadCount = 3;
        CountDownLatch latch = new CountDownLatch(threadCount);

        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 正在执行任务...");
                try {
                    Thread.sleep(1000); // 模拟任务耗时
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName() + " 被中断");
                }
                System.out.println(Thread.currentThread().getName() + " 任务完成");
                latch.countDown(); // 计数器减 1
            }).start();
        }

        System.out.println("主线程等待所有子线程完成...");
        latch.await(); // 主线程阻塞,直到计数器为 0
        System.out.println("所有子线程已完成,主线程继续执行");
    }
}

输出示例:

Thread-0 正在执行任务...
Thread-1 正在执行任务...
Thread-2 正在执行任务...
主线程等待所有子线程完成...
Thread-0 任务完成
Thread-1 任务完成
Thread-2 任务完成
所有子线程已完成,主线程继续执行

3. InterruptedException 出现的位置 #

  • await() 方法是一个可中断的阻塞方法,如果当前线程在等待过程中被中断,会立即抛出 InterruptedException,并清除线程的中断标志。
  • 这意味着你需要在调用 await() 的地方用 try-catch 捕获 InterruptedException,或者继续向上抛出。

示例:

try {
    latch.await();
} catch (InterruptedException e) {
    // 处理中断逻辑,比如恢复中断标志或退出
    Thread.currentThread().interrupt();
    System.out.println("主线程等待时被中断");
}

4. 总结 #

  • CountDownLatch 用于线程间的等待与通知,常用于主线程等待多个子任务完成。
  • await() 方法会阻塞当前线程,直到计数器为 0 或线程被中断。
  • 如果线程在 await() 阻塞期间被中断,会抛出 InterruptedException,需要正确处理。

参考:

awaitTermination 详解 #

awaitTermination 是 Java 线程池(如 ExecutorService)提供的一个方法,用于阻塞当前线程,等待线程池关闭

方法签名 #

boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException

作用 #

  • 当你调用 shutdown()shutdownNow() 后,线程池会进入关闭状态,但此时线程池中的任务可能还没有全部执行完毕。
  • awaitTermination 方法会让当前线程(通常是主线程)阻塞,直到以下三种情况之一发生:
    1. 线程池中的所有任务都执行完毕,线程池彻底关闭(此时返回 true)。
    2. 等待超时时间到达(此时返回 false)。
    3. 当前线程在等待期间被中断(此时抛出 InterruptedException)。

常见用法 #

ExecutorService executor = Executors.newFixedThreadPool(2);
// 提交任务...
executor.shutdown(); // 启动优雅关闭

try {
    // 最多等待10秒,直到线程池关闭
    boolean terminated = executor.awaitTermination(10, TimeUnit.SECONDS);
    if (terminated) {
        System.out.println("线程池已关闭,所有任务执行完毕");
    } else {
        System.out.println("超时,线程池仍未完全关闭");
    }
} catch (InterruptedException e) {
    System.out.println("等待线程池关闭时被中断");
    Thread.currentThread().interrupt();
}

注意事项 #

  • 必须先调用 shutdown()shutdownNow(),否则 awaitTermination 会一直等待。
  • 如果线程池在指定时间内没有关闭,方法会返回 false,你可以选择后续处理(如强制关闭等)。
  • 如果当前线程在等待期间被中断,会抛出 InterruptedException,需要捕获并处理。

典型场景 #

  • 主线程需要等待线程池中所有任务执行完毕后再继续后续操作(如资源释放、程序退出等)。
  • 结合 shutdown()awaitTermination() 实现线程池的优雅关闭。

总结 #

  • awaitTermination 用于等待线程池关闭,保证所有任务执行完毕或超时后再继续执行主流程。
  • 是多线程编程中线程池资源管理的重要

如何停止一个 Java 应用 #

Java 应用的停止方式主要有以下几种,常见场景和推荐做法如下:

1. 正常停止(优雅关闭) #

  • 主线程执行完毕:主方法(main)执行结束,且没有非守护线程在运行,JVM 会自动退出。
  • 关闭线程池:如果应用中有线程池,建议先调用 shutdown(),再用 awaitTermination() 等待所有任务完成,最后主线程退出。
  • 关闭资源:在退出前关闭数据库连接、文件流、网络连接等资源,避免资源泄漏。
  • 使用钩子线程:可以通过 Runtime.getRuntime().addShutdownHook(Thread hook) 注册关闭钩子,在 JVM 退出前执行清理逻辑。

示例:

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    System.out.println("应用即将关闭,执行清理工作...");
    // 关闭资源、保存状态等
}));

2. 强制停止 #

  • System.exit(int status):立即终止 JVM,status=0 表示正常退出,非0表示异常退出。会执行所有已注册的关闭钩子,但不会等待线程池任务完成。
  • kill 进程:在操作系统层面直接杀死 Java 进程(如 Linux 下 kill 命令),此方式不推荐,可能导致资源未释放、数据丢失。

示例:

System.exit(0); // 强制退出

3. 响应外部信号 #

  • 接收 SIGTERM/SIGINT 信号:如在容器、服务环境下,收到 kill、Ctrl+C 等信号时,JVM 会自动触发关闭钩子,应用可在钩子中优雅停机。

4. 停止线程的正确方式 #

  • 不要使用 Thread.stop()(已废弃,危险)。
  • 推荐通过设置中断标志(interrupt()),让线程在合适位置检测并响应中断,安全退出。

示例:

while (!Thread.currentThread().isInterrupted()) {
    // 业务逻辑
}

总结 #

  • 推荐优雅关闭:先关闭线程池和资源,再让主线程退出。
  • 可用关闭钩子做最后的清理。
  • 避免强制杀进程或使用已废弃的 Thread.stop()。
  • 多线程应用要确保所有线程都能正确响应中断和