Skip to content

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 时,始终恢复中断标志

    java
    Thread.currentThread().interrupt();
  • [ ] 不要吞掉 InterruptedException

    java
    // ❌ 错误
    catch (InterruptedException e) { /* 什么都不做 */ }
    
    // ✅ 正确
    catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new BusinessException("被中断");
    }
  • [ ] 长时间运行的循环定期检查中断

    java
    while (!Thread.currentThread().isInterrupted()) {
        // 业务逻辑
    }
  • [ ] 使用可中断的阻塞方法

    java
    lock.lockInterruptibly();  // 而不是 lock.lock()
  • [ ] 优雅停机时正确处理中断

    java
    executor.shutdown();
    executor.awaitTermination(30, TimeUnit.SECONDS);
  • [ ] 在 finally 块中清理资源

    java
    try {
        lock.lockInterruptibly();
        // 业务逻辑
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
  • [ ] 记录中断事件用于监控

    java
    log.warn("操作被中断: operation={}, thread={}",
            operation, Thread.currentThread().getName());

9. 参考资料 ​

  1. Java Concurrency in Practice - Brian Goetz

    • Chapter 7: Cancellation and Shutdown
  2. Effective Java (3rd Edition) - Joshua Bloch

    • Item 79: Avoid excessive synchronization
  3. JDK 源码

    • java.lang.Thread
    • java.util.concurrent.locks.AbstractQueuedSynchronizer
  4. 相关 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
维护者:技术团队