定义

ReentrantLock 实现了可中断性,这意味着线程在等待锁的过程中,可以被其他线程中断而提前结束等待。可打断的方法lockInterruptibly() 的行为与 Thread.sleep() 非常相似:

  1. Thread.sleep() 一样,会完全放弃用户态的CPU时间片,让操作系统调度其他线程。这是所有高效阻塞机制(sleep, wait, park, lock 等待)的共同核心原则:避免“忙等待”(Busy-Waiting),即在条件不满足时主动让出CPU,而不是空转浪费资源。

  2. 当线程在等待锁的阻塞过程中被中断,它会立即放弃等待,从阻塞处(lockInterruptibly() 调用点)抛出 InterruptedException

完整流程

  1. 线程状态 → 线程因 lock.lockInterruptibly() 在 AQS 队列中等待锁,调用 LockSupport.park() 后进入 WAITING 状态,并完全放弃 CPU。

  2. 外部中断 → 另一个线程(如主线程)调用 blockedThread.interrupt()

  3. 系统响应 → JVM 与操作系统内核协同,将该线程标记为可运行,并将其状态从 WAITING 改为 RUNNABLE

  4. 进入调度队列 → 线程被放入操作系统的 就绪队列,等待 CPU 调度。

  5. 获得 CPU 时间片 → 操作系统调度器在某个时刻为此线程分配 CPU 时间片

  6. 恢复执行点 → 线程获得 CPU 后,从当初阻塞的 LockSupport.park() 之后恢复执行,回到 AQS 框架(如 acquireInterruptibly 方法)的循环中。

  7. 检查中断标志 → AQS 代码立即检查线程的中断标志,发现其为 true(表示已被中断)。

  8. 执行抛出动作 → AQS 代码执行 throw new InterruptedException()。

  9. 异常传播InterruptedException 沿调用栈向上传播,最终跳出 lockInterruptibly() 方法。

  10. 用户处理 → 异常被用户代码中的 catch (InterruptedException e) 块捕获,线程在 catch 块中执行中断处理逻辑,并彻底放弃对锁的争夺。

import java.util.concurrent.locks.ReentrantLock;

public class LockInterruptiblyDemo {

    // 创建一个公平锁,便于观察线程进入等待队列
    private static final ReentrantLock lock = new ReentrantLock(true);

    public static void main(String[] args) throws InterruptedException {
        // 主线程先获取锁,确保后续线程都会阻塞
        System.out.println("[主线程] 我先拿到锁。");
        lock.lock();

        // 创建并启动一个会因等待锁而被阻塞的线程
        Thread blockedThread = new Thread(() -> {
            try {
                System.out.println("[子线程] 尝试通过 lockInterruptibly() 获取锁...");
                // 这里会阻塞,等待主线程释放锁
                lock.lockInterruptibly(); // 🟢 可中断的阻塞点,类似于 Thread.sleep()
                
                // 正常情况下(未被中断),获取到锁后执行这里
                try {
                    System.out.println("[子线程] 成功获取到锁并开始工作!");
                } finally {
                    lock.unlock();
                    System.out.println("[子线程] 工作完成,释放锁。");
                }
                
            } catch (InterruptedException e) {
                // 在等待锁的过程中被中断,会立即跳到这里执行
                System.out.println("[子线程] 在等待锁时被中断!放弃等待。");
                // 此时线程的中断状态已被清除 (Thread.interrupted() 返回 false)
                // 可以在这里做一些资源清理工作
            }
        });

        blockedThread.start();
        Thread.sleep(1000); // 确保子线程已经启动并进入AQS等待队列
        System.out.println("[主线程] 子线程已阻塞在等待队列中,我现在中断它。");
        
        blockedThread.interrupt(); // 🎯 关键操作:中断子线程
        
        Thread.sleep(500); // 给子线程一点时间处理中断
        System.out.println("[主线程] 我释放锁。");
        lock.unlock(); // 即使现在释放锁,被中断的子线程也不会来拿了
        
        blockedThread.join(); // 等待子线程完全结束
        System.out.println("[主线程] 演示结束。");
    }
}

运行上述代码,输出结果如下:

[主线程] 我先拿到锁。
[子线程] 尝试通过 lockInterruptibly() 获取锁...
[主线程] 子线程已阻塞在等待队列中,我现在中断它。
[子线程] 在等待锁时被中断!放弃等待。
[主线程] 我释放锁。
[主线程] 演示结束。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐