Java并发编程中的线程中断与停止机制

Java中禁用Thread.stop(),因其破坏锁和状态导致数据不一致;应使用interrupt()协作中断,并在循环中检查isInterrupted()或捕获InterruptedException后退出。

Java中不能用stop()强行终止线程

调用Thread.stop()会立即释放所有锁并破坏线程状态,极易导致数据不一致或死锁。JDK早在1.2就将其标记为@Deprecated,现代代码中绝对禁止使用。

常见错误现象包括:共享对象处于中间状态、synchronized块提前退出后锁未释放、JVM抛出ThreadDeath异常(该异常甚至无法被常规catch捕获)。

  • 替代方案只有协作式中断——依赖Thread.interrupt()和线程自身响应
  • stop()在任何JDK版本(包括Java 17+)中都不应出现在生产代码里
  • 某些老旧测试代码或IDE自动生成的模板里可能残留该调用,需手动清理

interrupt()到底做了什么

interrupt()只是设置线程的中断状态位,并不强制停掉线程。它的行为取决于线程当前所处状态:

  • 线程处于WAITINGTIMED_WAITING(如调用wait()join()sleep())时,会立即抛出InterruptedException,同时清除中断状态
  • 线程处于RUNNABLE状态时,仅设置isInterrupted()返回true,不抛异常,也不影响执行流
  • 若线程已终止,调用interrupt()无任何效果

关键点在于:中断本身不等于停止,它只是一个信号,是否响应、如何响应,完全由线程自己决定。

如何正确响应中断并安全退出

典型做法是在循环中定期检查中断状态,并主动退出。重点不是“捕获Interru

ptedException”,而是“尊重中断意图”。

while (!Thread.currentThread().isInterrupted()) {
    // 执行任务逻辑
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        // 清理资源后退出循环
        Thread.currentThread().interrupt(); // 恢复中断状态(可选)
        break;
    }
}
  • 不要忽略InterruptedException——空catch是严重错误
  • catch块中调用Thread.currentThread().interrupt(),是为了保留中断信号供上层判断(尤其在线程池中很重要)
  • 如果任务涉及IO或锁,需额外处理:例如java.nio.channels.InterruptibleChannel会在中断时关闭通道;ReentrantLock.lockInterruptibly()可响应中断
  • 避免在finally里重置中断状态——这会掩盖真实的中断请求

线程池中的中断容易被忽略的细节

提交到ExecutorService的任务,其Future.cancel(true)调用的正是目标线程的interrupt(),但很多开发者误以为这能“杀掉”任务。

  • 若任务未主动检查中断或未使用可中断API(如sleeplockInterruptibly),cancel(true)几乎无效
  • shutdownNow()会尝试中断所有正在运行的线程,但返回的List包含的是**尚未开始执行**的任务,不是被中断的任务
  • 使用CompletableFuture时,cancel(true)不会中断底层线程,只影响future的状态

真正可靠的取消,必须在任务逻辑内部做检查——外部中断只是触发器,不是终止器。