在Java并发编程中,线程中断(Thread Interruption)是一种协作式的中断机制,它允许一个线程通知另一个线程"建议"其停止当前操作。与强制终止线程的stop()方法不同,中断机制更加安全可控,被中断的线程可以自主决定如何响应中断请求。
每个Java线程内部都维护着一个boolean类型的中断状态标志:
java复制// 伪代码示意
public class Thread {
private volatile boolean interrupted = false;
}
当调用线程的interrupt()方法时,JVM会执行以下原子操作:
重要提示:中断操作不会直接停止线程运行,需要被中断线程主动检查并处理中断状态
Java提供了三个核心方法操作中断状态:
| 方法签名 | 作用 | 注意事项 |
|---|---|---|
| void interrupt() | 设置中断标志位 | 若线程阻塞会触发InterruptedException |
| boolean isInterrupted() | 检查中断状态 | 不会清除中断标志 |
| static boolean interrupted() | 检查并清除中断状态 | 静态方法,会重置当前线程的中断标志 |
典型误用示例:
java复制// 错误示范:连续调用interrupted()会导致第二次返回false
if (Thread.interrupted() && Thread.interrupted()) {
// 可能不会执行到这里
}
当线程在以下方法阻塞时,中断会触发InterruptedException:
处理模式示例:
java复制try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// 恢复中断状态(最佳实践)
Thread.currentThread().interrupt();
// 执行清理逻辑
}
为什么要在catch块中恢复中断状态?因为InterruptedException会清除中断标志,如果不恢复:
错误处理对比:
java复制// 错误方式:丢失中断信息
catch (InterruptedException e) {
// 仅打印日志
}
// 正确方式:恢复中断状态
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
对于CPU密集型任务,需要手动检查中断状态:
java复制while (!Thread.currentThread().isInterrupted()) {
// 执行计算任务
heavyCalculation();
// 定期检查
if (Thread.currentThread().isInterrupted()) {
// 清理资源
releaseResources();
break;
}
}
对于可能长时间运行的操作,建议拆分为可中断的片段:
java复制public void processLargeFile(File file) {
try (BufferedReader reader = new BufferedReader(...)) {
String line;
while ((line = reader.readLine()) != null) {
// 每处理100行检查一次中断
if (++lineCount % 100 == 0 && Thread.interrupted()) {
throw new InterruptedException();
}
processLine(line);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
handleInterruption();
}
}
ExecutorService提交任务后返回的Future对象提供取消机制:
java复制Future<?> future = executor.submit(task);
// 尝试取消任务(mayInterruptIfRunning=true表示发送中断)
future.cancel(true);
通过覆盖ThreadPoolExecutor的钩子方法实现定制中断逻辑:
java复制class CustomExecutor extends ThreadPoolExecutor {
@Override
protected void afterExecute(Runnable r, Throwable t) {
if (t == null && r instanceof Future<?>) {
try {
Future<?> future = (Future<?>) r;
if (future.isDone()) future.get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
if (t != null) handleException(t);
}
}
java复制// 错误:直接忽略中断
catch (InterruptedException e) {
// 什么也不做
}
java复制// 错误:错误线程上恢复中断
catch (InterruptedException e) {
otherThread.interrupt(); // 应该用currentThread()
}
java复制// 低效:连续两次检查
if (Thread.interrupted() || Thread.currentThread().isInterrupted()) {
...
}
对于极高频率的中断检查,可以使用局部变量缓存状态:
java复制// 优化前:每次循环都访问线程状态
while (!Thread.currentThread().isInterrupted()) {
// ...
}
// 优化后:适当降低检查频率
int localCounter = 0;
while (true) {
if (++localCounter % 1000 == 0) {
if (Thread.currentThread().isInterrupted()) break;
localCounter = 0;
}
// ...
}
通过组合模式实现灵活的中断控制:
java复制interface InterruptPolicy {
boolean shouldInterrupt();
}
class ThresholdPolicy implements InterruptPolicy {
private final int threshold;
private int count;
boolean shouldInterrupt() {
return ++count >= threshold || Thread.currentThread().isInterrupted();
}
}
在多层调用中正确传递中断状态:
java复制public void layer1() {
try {
layer2();
} catch (CustomInterruptedException e) {
Thread.currentThread().interrupt();
// 处理中断
}
}
private void layer2() throws CustomInterruptedException {
try {
blockingOperation();
} catch (InterruptedException e) {
throw new CustomInterruptedException(e);
}
}
结合Lock和Condition实现可中断的等待:
java复制Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
try {
lock.lock();
while (!conditionSatisfied && !Thread.currentThread().isInterrupted()) {
condition.await();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
建议的日志记录方式:
java复制private static final Logger logger = LoggerFactory.getLogger(...);
void handleInterrupt() {
logger.debug("Thread interrupted", new InterruptedException("Stack trace capture"));
// 注意:这里创建异常仅用于记录堆栈,不抛出
}
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 中断无效 | 未检查中断状态 | 在循环中添加isInterrupted()检查 |
| 异常丢失 | catch块未恢复中断 | 在catch块调用interrupt() |
| 性能下降 | 检查过于频繁 | 降低检查频率或使用优化模式 |
| 死锁 | 中断后未释放锁 | 确保finally块释放所有资源 |
bash复制# 查看线程状态示例
jstack <pid> | grep -A10 "interrupted"