Java 线程中断处理深度解析与实战
作者:墨与星辰
创建时间:2025-11-06
适用场景:分布式系统、高并发业务、线程池管理
目录
1. 线程中断机制基础
1.1 什么是线程中断?
线程中断(Thread Interruption)是 Java 提供的一种协作式线程取消机制。
java
// 核心 API
Thread thread = new Thread(() -> {
// 业务逻辑
});
thread.start();
thread.interrupt(); // ← 请求中断线程关键特性:
- ✅ 协作式:需要线程主动检查并响应中断
- ✅ 非强制:不会强制停止线程(与
Thread.stop()不同) - ✅ 标志位:通过布尔标志传递中断信号
1.2 中断标志(Interrupt Status)
每个线程都有一个中断标志位:
java
public class Thread {
private volatile boolean interrupted = false; // 内部标志位
// 设置中断标志
public void interrupt() {
interrupted = true;
}
// 检查中断标志(不清除)
public boolean isInterrupted() {
return interrupted;
}
// 检查并清除中断标志
public static boolean interrupted() {
boolean oldValue = currentThread().interrupted;
currentThread().interrupted = false; // 清除
return oldValue;
}
}三个核心方法对比:
| 方法 | 检查标志 | 清除标志 | 静态方法 | 作用对象 |
|---|---|---|---|---|
interrupt() | ❌ | ❌ | ❌ | 设置中断标志 |
isInterrupted() | ✅ | ❌ | ❌ | 检查当前线程 |
interrupted() | ✅ | ✅ | ✅ | 检查并清除 |
1.3 中断的传播机制
java
// 中断信号的传播链
主线程 → interrupt()
↓
子线程检测到中断标志
↓
抛出 InterruptedException
↓
catch 块处理
↓
恢复中断标志 Thread.currentThread().interrupt()
↓
上层调用者感知中断2. InterruptedException 深度解析
2.1 为什么会抛出 InterruptedException?
某些阻塞方法在检测到中断时会抛出此异常:
java
// 会抛出 InterruptedException 的常见方法
Thread.sleep(1000); // 睡眠
Object.wait(); // 等待
Lock.lockInterruptibly(); // 可中断锁
BlockingQueue.take(); // 阻塞队列
Future.get(); // 等待结果
CountDownLatch.await(); // 等待计数
Semaphore.acquire(); // 获取信号量抛出异常时的行为:
java
public void sleep(long millis) throws InterruptedException {
if (Thread.interrupted()) { // ← 检查中断标志
// 1. 清除中断标志
Thread.currentThread().interrupted = false;
// 2. 抛出异常
throw new InterruptedException();
}
// 否则正常睡眠
}⚠️ 重要:抛出 InterruptedException 时会自动清除中断标志!
2.2 中断标志的自动清除
java
// 示例:中断标志的变化
Thread thread = new Thread(() -> {
System.out.println("1. 中断前: " + Thread.currentThread().isInterrupted()); // false
try {
Thread.sleep(10000); // 阻塞中被中断
} catch (InterruptedException e) {
System.out.println("2. catch 内: " + Thread.currentThread().isInterrupted()); // false (已清除)
}
System.out.println("3. catch 外: " + Thread.currentThread().isInterrupted()); // false
});
thread.start();
Thread.sleep(100);
thread.interrupt(); // 中断线程
// 输出:
// 1. 中断前: false
// 2. catch 内: false ← 注意:标志已被清除
// 3. catch 外: false关键点:
- 抛出
InterruptedException前,JVM 会清除中断标志 - 这导致上层代码无法通过
isInterrupted()检测到中断 - 必须手动恢复中断标志!
2.3 为什么要恢复中断标志?
java
// 场景:调用链
public void handleRequest() {
try {
processOrder(); // ← 内部可能被中断
sendNotification(); // ← 如果中断信号丢失,这里会继续执行!
} finally {
cleanup(); // ← 需要知道是否被中断来决定清理策略
}
}
private void processOrder() throws InterruptedException {
// 获取分布式锁
lock.lockInterruptibly(); // 可能抛出 InterruptedException
try {
// 处理订单
} finally {
lock.unlock();
}
}不恢复中断标志的问题:
java
// ❌ 错误:吞掉中断
private void processOrder() {
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
log.error("被中断", e); // 只记录日志
// 没有恢复中断标志!
}
}
// 结果:上层的 handleRequest() 不知道发生了中断
// - sendNotification() 会继续执行(可能不符合预期)
// - cleanup() 无法区分正常完成还是被中断正确做法:
java
// ✅ 正确:恢复中断标志
private void processOrder() {
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // ← 恢复中断标志
log.error("被中断", e);
throw new BusinessException("订单处理被中断");
}
}
// 上层代码可以检测到中断
public void handleRequest() {
try {
processOrder();
} catch (BusinessException e) {
if (Thread.currentThread().isInterrupted()) { // ← 可以检测到
log.warn("请求被中断,跳过通知");
return;
}
sendNotification();
}
}3. 中断处理的最佳实践
3.1 标准处理模式
java
/**
* 标准中断处理模式
*/
public void standardInterruptHandling() {
try {
// 可能抛出 InterruptedException 的操作
doSomethingInterruptible();
} catch (InterruptedException e) {
// 第一步:恢复中断标志
Thread.currentThread().interrupt();
// 第二步:记录日志
log.error("操作被中断: {}", e.getMessage());
// 第三步:转换为运行时异常或返回错误
throw new BusinessException("操作被中断");
}
}3.2 响应中断的四种策略
策略 1:传播中断异常(推荐)
java
// 场景:中间层方法
public void middleLayerMethod() throws InterruptedException {
// 直接向上抛出 InterruptedException
Thread.sleep(1000);
}
// 优点:简单直接,调用者自己决定如何处理
// 缺点:方法签名必须声明 throws InterruptedException策略 2:恢复中断标志并抛出运行时异常
java
// 场景:不能声明 InterruptedException(如 Runnable.run())
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复标志
throw new RuntimeException("被中断", e); // 抛出运行时异常
}
}
// 优点:适用于不能 throws 检查异常的场景
// 缺点:调用者需要处理运行时异常策略 3:恢复标志并返回错误码
java
// 场景:返回值明确的方法
public boolean transferMoney(Account from, Account to, BigDecimal amount) {
try {
lock.lockInterruptibly();
try {
// 转账逻辑
return true;
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("转账被中断");
return false; // 返回失败
}
}
// 优点:不破坏方法签名,清晰表达失败
// 缺点:需要调用者检查返回值策略 4:循环重试(特殊场景)
java
// 场景:必须完成的操作(谨慎使用)
public void criticalOperation() {
boolean interrupted = false;
try {
while (true) {
try {
// 必须完成的操作
doSomething();
break; // 成功后退出
} catch (InterruptedException e) {
// 记录中断,但继续重试
interrupted = true;
log.warn("操作被中断,重试中...");
}
}
} finally {
// 最后恢复中断标志
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
// 优点:确保关键操作完成
// 缺点:可能导致无法取消的线程3.3 分布式锁场景的完整实现
java
/**
* 完整的分布式锁中断处理示例
*/
@Service
@Slf4j
public class QuotaService {
@Resource
private RedissonClient redissonClient;
/**
* 扣减额度(完整的中断处理)
*/
public boolean deductQuota(Long tenantId, int amount) {
String lockKey = "quota:lock:" + tenantId;
RLock lock = redissonClient.getLock(lockKey);
try {
// 1. 尝试获取锁(可中断)
boolean locked = lock.tryLock(5, 30, TimeUnit.SECONDS);
if (!locked) {
throw new BusinessException("获取锁超时");
}
try {
// 2. 业务逻辑
return performDeduction(tenantId, amount);
} finally {
// 3. 释放锁(必须在 finally 中)
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
} catch (InterruptedException e) {
// 4. 中断处理三步曲
// Step 1: 恢复中断标志
Thread.currentThread().interrupt();
// Step 2: 记录详细日志
log.error("额度扣减被中断: tenantId={}, amount={}, thread={}",
tenantId, amount, Thread.currentThread().getName(), e);
// Step 3: 转换为业务异常
throw new BusinessException("额度扣减被中断,请重试");
}
}
/**
* 实际的扣减逻辑
*/
private boolean performDeduction(Long tenantId, int amount) {
// 业务逻辑...
return true;
}
}4. 实战场景分析
4.1 场景 1:HTTP 请求超时取消
java
/**
* Spring MVC 场景:用户取消请求
*/
@RestController
@Slf4j
public class OrderController {
@PostMapping("/orders")
public BaseResponse<OrderVO> createOrder(@RequestBody OrderRequest request) {
try {
// 创建订单(可能耗时)
OrderVO order = orderService.createOrder(request);
return BaseResponse.success(order);
} catch (BusinessException e) {
// 检查是否因中断导致
if (Thread.currentThread().isInterrupted()) {
log.warn("请求被取消: userId={}, requestId={}",
request.getUserId(), request.getRequestId());
return BaseResponse.error("请求已取消");
}
log.error("订单创建失败", e);
return BaseResponse.error(e.getMessage());
}
}
}
@Service
public class OrderService {
public OrderVO createOrder(OrderRequest request) {
try {
// 1. 锁库存
lockStock(request);
// 2. 创建订单
Order order = saveOrder(request);
// 3. 调用支付
payOrder(order);
return toVO(order);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断
throw new BusinessException("订单创建被中断");
}
}
private void lockStock(OrderRequest request) throws InterruptedException {
RLock lock = redissonClient.getLock("stock:" + request.getProductId());
// 使用可中断的锁
if (!lock.tryLock(3, 10, TimeUnit.SECONDS)) {
throw new BusinessException("库存锁定失败");
}
try {
// 扣减库存...
} finally {
lock.unlock();
}
}
}用户取消请求的流程:
1. 用户点击"取消"或关闭页面
↓
2. HTTP 连接断开
↓
3. Servlet 容器中断处理线程
↓
4. tryLock() 抛出 InterruptedException
↓
5. 恢复中断标志并向上传播
↓
6. Controller 检测到中断并返回取消响应4.2 场景 2:线程池任务取消
java
/**
* 线程池场景:通过 Future 取消任务
*/
@Service
@Slf4j
public class ReportService {
@Resource
private ThreadPoolExecutor reportExecutor;
/**
* 生成报表(可取消)
*/
public Future<Report> generateReport(Long tenantId) {
return reportExecutor.submit(() -> {
try {
log.info("开始生成报表: tenantId={}", tenantId);
// 1. 查询数据
List<Data> data = queryData(tenantId);
// 2. 计算统计(检查中断)
Statistics stats = calculateStatistics(data);
// 3. 生成报表
Report report = buildReport(stats);
log.info("报表生成完成: tenantId={}", tenantId);
return report;
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断
log.warn("报表生成被取消: tenantId={}", tenantId);
throw new CancellationException("报表生成被取消");
}
});
}
/**
* 计算统计(定期检查中断)
*/
private Statistics calculateStatistics(List<Data> data) throws InterruptedException {
Statistics stats = new Statistics();
for (int i = 0; i < data.size(); i++) {
// 每处理 1000 条检查一次中断
if (i % 1000 == 0) {
if (Thread.interrupted()) { // 检查并清除标志
throw new InterruptedException("计算被中断");
}
}
stats.add(data.get(i));
}
return stats;
}
}
// 使用示例
Future<Report> future = reportService.generateReport(10000001L);
// 用户取消操作
future.cancel(true); // 参数 true 表示中断线程
try {
Report report = future.get();
} catch (CancellationException e) {
log.info("报表生成已取消");
}4.3 场景 3:优雅停机
java
/**
* 服务优雅停机场景
*/
@Component
@Slf4j
public class GracefulShutdownHandler implements ApplicationListener<ContextClosedEvent> {
@Resource
private ThreadPoolExecutor businessExecutor;
@Resource
private List<LongRunningTask> longRunningTasks;
@Override
public void onApplicationEvent(ContextClosedEvent event) {
log.info("开始优雅停机...");
// 1. 停止接收新任务
businessExecutor.shutdown();
// 2. 中断所有长时间运行的任务
longRunningTasks.forEach(task -> {
log.info("中断任务: {}", task.getName());
task.interrupt();
});
try {
// 3. 等待现有任务完成(最多30秒)
if (!businessExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
log.warn("等待超时,强制关闭");
// 4. 强制中断所有任务
businessExecutor.shutdownNow();
// 5. 再等待10秒
if (!businessExecutor.awaitTermination(10, TimeUnit.SECONDS)) {
log.error("线程池无法正常关闭");
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("停机过程被中断", e);
businessExecutor.shutdownNow();
}
log.info("优雅停机完成");
}
}
/**
* 可中断的长时间任务
*/
@Component
public class DataSyncTask implements LongRunningTask, Runnable {
private volatile Thread taskThread;
@Override
public void run() {
taskThread = Thread.currentThread();
try {
while (!Thread.currentThread().isInterrupted()) {
// 1. 同步一批数据
syncBatch();
// 2. 短暂休息
Thread.sleep(1000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.info("数据同步任务被中断");
} finally {
// 清理资源
cleanup();
log.info("数据同步任务退出");
}
}
@Override
public void interrupt() {
if (taskThread != null) {
taskThread.interrupt();
}
}
private void syncBatch() throws InterruptedException {
// 检查中断
if (Thread.interrupted()) {
throw new InterruptedException();
}
// 同步逻辑...
}
}5. 常见错误与反模式
5.1 ❌ 反模式 1:吞掉异常
java
// ❌ 错误:完全忽略中断
public void badExample1() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// 什么都不做!
}
}
// 问题:
// 1. 中断信号丢失
// 2. 上层调用者无法感知中断
// 3. 可能导致线程无法正常退出5.2 ❌ 反模式 2:只记录日志
java
// ❌ 错误:只记录日志,不恢复中断标志
public void badExample2() {
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
log.error("被中断了", e); // 只记录
// 没有恢复中断标志!
}
}
// 问题:
// 1. isInterrupted() 返回 false
// 2. 后续代码无法检测到中断
// 3. 可能继续执行不应该执行的逻辑5.3 ❌ 反模式 3:捕获后继续循环
java
// ❌ 错误:捕获中断后继续循环
public void badExample3() {
while (true) {
try {
doWork();
Thread.sleep(1000);
} catch (InterruptedException e) {
log.warn("被中断,继续执行");
// 继续循环!
}
}
}
// 问题:
// 1. 无法通过中断停止线程
// 2. 优雅停机失败
// 3. 可能造成资源泄漏正确做法:
java
// ✅ 正确:检查中断标志
public void goodExample() {
while (!Thread.currentThread().isInterrupted()) {
try {
doWork();
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复标志
log.info("任务被中断,退出循环");
break; // 退出循环
}
}
}5.4 ❌ 反模式 4:捕获错误的异常
java
// ❌ 错误:捕获所有异常
public void badExample4() {
try {
Thread.sleep(1000);
} catch (Exception e) { // 捕获了 Exception
log.error("出错了", e);
// InterruptedException 被吞掉了!
}
}
// ✅ 正确:分别捕获
public void goodExample() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("被中断", e);
} catch (Exception e) {
log.error("业务异常", e);
}
}5.5 ❌ 反模式 5:使用 Thread.stop()
java
// ❌ 错误:使用已废弃的 stop()
@Deprecated
public void badExample5() {
thread.stop(); // 危险!
}
// 问题:
// 1. 强制停止线程,可能导致数据不一致
// 2. 不会释放持有的锁
// 3. 可能导致对象状态损坏
// ✅ 正确:使用中断
public void goodExample() {
thread.interrupt(); // 请求中断,让线程自己处理
}6. 分布式场景应用
6.1 分布式锁超时处理
java
/**
* 分布式锁场景的完整实现
*/
@Service
@Slf4j
public class DistributedLockService {
@Resource
private RedissonClient redissonClient;
/**
* 执行带锁的操作(完整的中断和超时处理)
*/
public <T> T executeWithLock(
String lockKey,
Supplier<T> action,
long waitTime,
long leaseTime,
TimeUnit unit) {
RLock lock = redissonClient.getLock(lockKey);
boolean locked = false;
try {
// 1. 尝试获取锁(可中断)
locked = lock.tryLock(waitTime, leaseTime, unit);
if (!locked) {
log.warn("获取锁超时: lockKey={}, waitTime={}{}",
lockKey, waitTime, unit);
throw new LockTimeoutException("获取锁超时");
}
log.debug("成功获取锁: lockKey={}", lockKey);
// 2. 执行业务逻辑
T result = action.get();
log.debug("业务执行完成: lockKey={}", lockKey);
return result;
} catch (InterruptedException e) {
// 3. 中断处理
Thread.currentThread().interrupt();
log.error("锁操作被中断: lockKey={}, thread={}, interrupted={}",
lockKey,
Thread.currentThread().getName(),
Thread.currentThread().isInterrupted(),
e);
throw new BusinessException("操作被中断");
} finally {
// 4. 释放锁(必须在 finally 中)
if (locked && lock.isHeldByCurrentThread()) {
lock.unlock();
log.debug("释放锁: lockKey={}", lockKey);
}
}
}
/**
* 使用示例
*/
public void deductQuota(Long tenantId, int amount) {
String lockKey = "quota:lock:" + tenantId;
executeWithLock(
lockKey,
() -> {
// 业务逻辑
performDeduction(tenantId, amount);
return null;
},
5, 30, TimeUnit.SECONDS
);
}
}6.2 异步任务的中断传播
java
/**
* CompletableFuture 场景的中断传播
*/
@Service
@Slf4j
public class AsyncTaskService {
@Resource
private Executor asyncExecutor;
/**
* 异步执行任务(支持中断)
*/
public CompletableFuture<Result> executeAsync(Task task) {
// 记录当前线程的中断状态
Thread callerThread = Thread.currentThread();
return CompletableFuture.supplyAsync(() -> {
try {
// 检查调用线程是否被中断
if (callerThread.isInterrupted()) {
log.warn("调用线程已中断,取消任务: task={}", task.getId());
throw new CancellationException("任务已取消");
}
// 执行任务
return doExecute(task);
} catch (InterruptedException e) {
// 恢复当前线程的中断标志
Thread.currentThread().interrupt();
// 同时尝试中断调用线程(如果需要)
if (!callerThread.isInterrupted()) {
callerThread.interrupt();
}
throw new CompletionException("任务被中断", e);
}
}, asyncExecutor);
}
/**
* 执行任务逻辑(定期检查中断)
*/
private Result doExecute(Task task) throws InterruptedException {
Result result = new Result();
for (Step step : task.getSteps()) {
// 每一步检查中断
if (Thread.interrupted()) {
throw new InterruptedException("步骤被中断: " + step.getName());
}
// 执行步骤
result.addStepResult(executeStep(step));
}
return result;
}
}6.3 定时任务的中断处理
java
/**
* XXL-Job 场景的中断处理
*/
@Component
@Slf4j
public class DataSyncJob {
/**
* XXL-Job 任务执行器
*/
@XxlJob("dataSyncJobHandler")
public void syncData() {
log.info("开始数据同步任务");
// 获取分片参数
int shardIndex = XxlJobHelper.getShardIndex();
int shardTotal = XxlJobHelper.getShardTotal();
try {
// 执行同步(可中断)
performSync(shardIndex, shardTotal);
log.info("数据同步完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("数据同步被中断: shardIndex={}/{}, interrupted={}",
shardIndex, shardTotal,
Thread.currentThread().isInterrupted());
// 通知 XXL-Job 任务失败
XxlJobHelper.handleFail("任务被中断");
} catch (Exception e) {
log.error("数据同步失败", e);
XxlJobHelper.handleFail(e.getMessage());
}
}
/**
* 执行同步逻辑
*/
private void performSync(int shardIndex, int shardTotal) throws InterruptedException {
// 查询需要同步的数据
List<Long> dataIds = queryDataIds(shardIndex, shardTotal);
log.info("待同步数据量: {}", dataIds.size());
int successCount = 0;
int failCount = 0;
for (int i = 0; i < dataIds.size(); i++) {
// 每处理 100 条检查一次中断
if (i % 100 == 0) {
if (Thread.interrupted()) {
log.warn("检测到中断信号,停止同步: processed={}/{}", i, dataIds.size());
throw new InterruptedException("同步被中断");
}
// 更新进度
XxlJobHelper.log("同步进度: {}/{}", i, dataIds.size());
}
try {
syncOne(dataIds.get(i));
successCount++;
} catch (Exception e) {
failCount++;
log.error("同步失败: dataId={}", dataIds.get(i), e);
}
}
log.info("同步结果: success={}, fail={}", successCount, failCount);
XxlJobHelper.handleSuccess("同步完成: success=" + successCount + ", fail=" + failCount);
}
}7. 性能与监控
7.1 中断相关的监控指标
java
/**
* 中断监控
*/
@Component
@Slf4j
public class InterruptMonitor {
private final AtomicLong interruptCount = new AtomicLong(0);
private final AtomicLong timeoutCount = new AtomicLong(0);
/**
* 记录中断事件
*/
public void recordInterrupt(String operation, Thread thread) {
interruptCount.incrementAndGet();
log.warn("中断事件: operation={}, thread={}, stackTrace={}",
operation,
thread.getName(),
getShortStackTrace(thread));
// 发送监控指标
sendMetric("thread.interrupt.count", 1,
"operation", operation,
"thread", thread.getName());
}
/**
* 记录超时事件
*/
public void recordTimeout(String operation, long waitTime, TimeUnit unit) {
timeoutCount.incrementAndGet();
log.warn("超时事件: operation={}, waitTime={}{}",
operation, waitTime, unit);
// 发送监控指标
sendMetric("thread.timeout.count", 1,
"operation", operation,
"waitTime", String.valueOf(unit.toMillis(waitTime)));
}
/**
* 获取统计信息
*/
public Map<String, Object> getStats() {
return Map.of(
"interruptCount", interruptCount.get(),
"timeoutCount", timeoutCount.get(),
"timestamp", System.currentTimeMillis()
);
}
}7.2 线程池监控
java
/**
* 线程池中断监控
*/
@Configuration
public class ThreadPoolMonitorConfig {
@Bean
public ThreadPoolExecutor monitoredExecutor() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, 20,
60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000),
new MonitoredThreadFactory("business"),
new MonitoredRejectedHandler()
);
// 启动监控
startMonitoring(executor);
return executor;
}
/**
* 启动线程池监控
*/
private void startMonitoring(ThreadPoolExecutor executor) {
ScheduledExecutorService monitor = Executors.newScheduledThreadPool(1);
monitor.scheduleAtFixedRate(() -> {
// 监控线程池状态
log.info("线程池监控: active={}, poolSize={}, queueSize={}, completed={}",
executor.getActiveCount(),
executor.getPoolSize(),
executor.getQueue().size(),
executor.getCompletedTaskCount());
// 检查是否有线程被中断
Thread[] threads = new Thread[executor.getPoolSize()];
Thread.enumerate(threads);
for (Thread thread : threads) {
if (thread != null && thread.isInterrupted()) {
log.warn("发现被中断的线程: name={}, state={}",
thread.getName(), thread.getState());
}
}
}, 0, 30, TimeUnit.SECONDS);
}
}
/**
* 监控线程工厂
*/
class MonitoredThreadFactory implements ThreadFactory {
private final String namePrefix;
private final AtomicInteger threadNumber = new AtomicInteger(1);
public MonitoredThreadFactory(String namePrefix) {
this.namePrefix = namePrefix;
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, namePrefix + "-" + threadNumber.getAndIncrement());
// 设置异常处理器
t.setUncaughtExceptionHandler((thread, e) -> {
log.error("线程异常: thread={}, interrupted={}",
thread.getName(),
thread.isInterrupted(),
e);
});
return t;
}
}8. 总结与最佳实践 Checklist
✅ 中断处理 Checklist
[ ] 捕获 InterruptedException 时,始终恢复中断标志
javaThread.currentThread().interrupt();[ ] 不要吞掉 InterruptedException
java// ❌ 错误 catch (InterruptedException e) { /* 什么都不做 */ } // ✅ 正确 catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new BusinessException("被中断"); }[ ] 长时间运行的循环定期检查中断
javawhile (!Thread.currentThread().isInterrupted()) { // 业务逻辑 }[ ] 使用可中断的阻塞方法
javalock.lockInterruptibly(); // 而不是 lock.lock()[ ] 优雅停机时正确处理中断
javaexecutor.shutdown(); executor.awaitTermination(30, TimeUnit.SECONDS);[ ] 在 finally 块中清理资源
javatry { lock.lockInterruptibly(); // 业务逻辑 } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } }[ ] 记录中断事件用于监控
javalog.warn("操作被中断: operation={}, thread={}", operation, Thread.currentThread().getName());
9. 参考资料
Java Concurrency in Practice - Brian Goetz
- Chapter 7: Cancellation and Shutdown
Effective Java (3rd Edition) - Joshua Bloch
- Item 79: Avoid excessive synchronization
JDK 源码
java.lang.Threadjava.util.concurrent.locks.AbstractQueuedSynchronizer
相关 JEP
- JEP 425: Virtual Threads (Preview)
附录:快速参考
A. 中断状态检查方法对比
| 方法 | 语法 | 清除标志 | 静态 | 推荐场景 |
|---|---|---|---|---|
isInterrupted() | thread.isInterrupted() | ❌ | ❌ | 检查其他线程 |
interrupted() | Thread.interrupted() | ✅ | ✅ | 检查当前线程并清除 |
checkInterrupted() | if (isInterrupted()) throw | ❌ | ❌ | 抛出异常前检查 |
B. 常见阻塞方法
会抛出 InterruptedException:
Thread.sleep()Object.wait()Lock.lockInterruptibly()Condition.await()BlockingQueue.take/put()CountDownLatch.await()Semaphore.acquire()Future.get()
不会抛出 InterruptedException:
Lock.lock()(使用lockInterruptibly()代替)synchronized(无法中断)- 文件 I/O (使用 NIO 的可中断通道)
- Socket I/O (使用 NIO)
文档版本:v1.0
最后更新:2025-11-06
维护者:技术团队