如何停止一个线程?

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()实例方法,查询线程的中断状态,不会清除标志。
相关推荐
iOS开发上架哦2 分钟前
移动端网页调试实战,键盘弹出与视口错位问题的定位与优化
后端
百度Geek说6 分钟前
PaddleMIX推出扩散模型推理加速Fast-Diffusers:自研蒸馏加速方法FLUX-Lightning实现4步图像生成
后端
gopher_looklook14 分钟前
Go并发实战:singleflight 源码解读与二次封装
数据结构·后端·go
用户8338102512222 分钟前
我为什么做PmMock:让接口设计不再头疼
前端·后端
二闹29 分钟前
IService 和 BaseMapper:CRUD 操作的选择指南
后端
dylan_QAQ30 分钟前
【附录】Spring AOP 基础知识及应用
后端·spring
Java中文社群42 分钟前
抱歉!Java面试标准答案最不重要
java·后端·面试
jiguanghover1 小时前
n8n 创建多维表格犯的错误
前端·后端
dylan_QAQ1 小时前
【附录】Spring 配置属性绑定 基础及应用
后端·spring
泡海椒1 小时前
jquick Path:让JSON数据处理像呼吸一样简单
后端