如何停止一个线程?

1. 不要用的方法

1.1 Thread.stop()

  • 早已废弃 (@Deprecated),会直接释放线程持有的所有锁。
  • 可能导致对象处于不一致状态(例如正在写一个共享变量,被 stop 中断,数据可能损坏)。
  • 可能引发死锁或数据不一致。

1.2 Thread.suspend() / Thread.resume()

  • 同样已废弃,容易在持锁时挂起线程,导致永久死锁。

2. 推荐的线程停止方式

Java 规范倡导所谓 "协作式中断" (Cooperative Interruption):

→ 不强制杀死线程,而是给线程一个信号,让它自己停下来。

核心思路:

  1. 外部线程设置某种"停止标记"或调用 interrupt()。
  2. 线程内部定期检查标记,当检测到需要停止时,自己安全退出。

2.1 方法一:使用标志位

java 复制代码
public class StopThreadDemo implements Runnable { 
    private volatile boolean stopRequested = false; 
    public void stop() { stopRequested = true; } 
    @Override public void run() { 
        while (!stopRequested) { // 线程工作 System.out.println("线程运行中..."); 
            try { Thread.sleep(500); } 
            catch (InterruptedException e) { // 如果需要响应中断,可在这里 break 
                Thread.currentThread().interrupt(); // 保留中断状态 } } 
       System.out.println("线程安全停止"); } 
public static void main(String[] args) throws InterruptedException { 
    StopThreadDemo task = new StopThreadDemo(); 
    Thread t = new Thread(task); 
    t.start(); 
    Thread.sleep(2000); 
    task.stop(); // 通知线程停止 } }

要点:

  • volatile 确保多线程可见性,避免被编译器优化成缓存变量。
  • 线程内部必须自己在循环条件中检查 stopRequested。

2.2 方法二:使用 interrupt() + 检查中断状态

当线程处于阻塞状态(sleep()、wait()、join())时,调用 interrupt() 会抛 InterruptedException。

java 复制代码
public class InterruptDemo implements Runnable { 
    @Override public void run() { 
        while (!Thread.currentThread().isInterrupted()) { 
            System.out.println("线程运行中..."); 
            try { 
                Thread.sleep(500); } 
            catch (InterruptedException e) { 
                System.out.println("线程被中断,准备停止..."); 
                Thread.currentThread().interrupt(); // 重新设置中断状态(因为异常会清除中断标记) 
                break; } } 
            System.out.println("线程安全退出"); } 
public static void main(String[] args) throws InterruptedException { 
    Thread t = new Thread(new InterruptDemo()); 
    t.start(); Thread.sleep(2000); 
    t.interrupt(); // 通知线程停止(协作式) } }

要点:

  • interrupt() 并不会强制杀死线程,而是给线程一个 中断标记。
  • 对阻塞方法,interrupt() 会让它抛 InterruptedException。
  • 线程必须捕获并自己退出(或在循环判断中断标记时退出)。

2.3 方法三:结合 ExecutorService 提交任务

使用 线程池 API,可以优雅地停止线程:

java 复制代码
ExecutorService executor = Executors.newSingleThreadExecutor(); 
Future<?> future = executor.submit(() -> { 
    try { 
        while (!Thread.currentThread().isInterrupted()) { // 工作代码   
                Thread.sleep(500); } } 
    catch (InterruptedException e) { 
        Thread.currentThread().interrupt(); 
     } }); 
        Thread.sleep(2000); 
        future.cancel(true); // true 表示试图中断线程 executor.shutdown();
  • future.cancel(true) 相当于调用底层线程的 interrupt()。

3. 底层原理

  • 每个线程对象里有一个 中断标志位 (interrupted)。
  • interrupt() 只是把标志位置为 true,对阻塞方法则会抛 InterruptedException。
  • Thread.interrupted():静态方法,返回当前线程的中断状态,并清除标志位。
  • isInterrupted():实例方法,查询线程的中断状态,不会清除标志。
相关推荐
陈随易2 小时前
Postman 被曝自动往 Claude Code 和 Codex 装插件
前端·后端·程序员
云-缩影4 小时前
拆解 AI 图像生成应用的后端架构:ECS、OSS 和大模型 API 怎么配合
java·spring boot·后端
考虑考虑5 小时前
Posrgresql恢复.backup文件
数据库·后端·postgresql
无奈何杨6 小时前
风控决策系统决策流,策略模式,权重、顺序、投票、最坏
后端
Patrick_Wilson7 小时前
Go 为什么需要 defer?从函数怎么收尾说起
后端·go·编程语言
苏三说技术7 小时前
DeepSeek Harness入驻B站,评论区被干成了大型插件集市!
后端
橙序员小站7 小时前
我一句话让 AI 把公众号工作台搭完了:部署实测记录
后端
Patrick_Wilson7 小时前
从文本输出理解 Go 方法与接口
后端·go·编程语言
QCoding8 小时前
企业级Agent Evaluation实战:Agent能跑就能上线?AI Agent到底怎么测?
后端·spring
苍何9 小时前
让 WorkBuddy 操控浏览器,干活有点猛啊1
后端