电脑学堂
第二套高阶模板 · 更大气的阅读体验

ReentrantLock可中断特性:让线程等待不再“死等”

发布时间:2025-12-14 00:46:53 阅读:14 次

在写多ref="/tag/413/" style="color:#C468A7;font-weight:bold;">线程程序时,很多人用过 synchronized,但可能对 ReentrantLock 的细节了解不多。特别是它的可中断特性,其实是个很实用的功能,能避免线程陷入无休止的等待。

什么是 ReentrantLock 的可中断特性?

简单说,就是当一个线程正在等待获取锁的时候,如果它被其他线程调用了 interrupt() 方法,它可以立即停止等待,转而去处理中断信号。这跟 synchronized 不一样,synchronized 是无法响应中断的,一旦开始等,就得等到拿到锁为止,哪怕你已经不想等了。

举个生活中的例子:你在银行排队办业务,前面人特别多。如果是 synchronized 模式,你就得一直排下去,哪怕突然接到电话说家里有急事,你也只能硬着头皮等。但如果是 ReentrantLock 的 lockInterruptibly(),你可以选择“我不等了”,直接离开队伍,去处理更紧急的事。

代码怎么用?

关键在于使用 lockInterruptibly() 方法,而不是普通的 lock()

ReentrantLock lock = new ReentrantLock();

Thread t1 = new Thread(() -> {
    try {
        lock.lockInterruptibly(); // 可中断地获取锁
        try {
            // 执行临界区操作
            System.out.println("线程 " + Thread.currentThread().getName() + " 正在工作...");
            Thread.sleep(5000); // 模拟耗时操作
        } finally {
            lock.unlock();
        }
    } catch (InterruptedException e) {
        System.out.println("线程 " + Thread.currentThread().getName() + " 被中断,不再等待锁");
    }
}, "t1");

Thread t2 = new Thread(() -> {
    lock.lock();
    try {
        System.out.println("线程 t2 拿到了锁,开始工作...");
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        lock.unlock();
    }
}, "t2");

t1.start();
t2.start();

// 让 t1 等一会后强制中断
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

t1.interrupt(); // 中断 t1

运行这段代码,你会发现 t1 并没有傻等 10 秒,而是在被中断后立刻退出了等待状态,打印出“被中断”的提示。这就是可中断特性的实际效果。

什么时候该用它?

当你写的任务是有时间限制的,比如网络请求超时、用户主动取消操作、后台定时任务清理资源,这时候用 lockInterruptibly() 就很有必要。它让程序更灵活,也能避免资源浪费。

比如你开发一个文件上传功能,用户点了“取消”。如果上传线程正卡在某个锁上等,而这个锁不支持中断,那线程还得继续等下去,直到拿到锁才可能检查到取消标志——用户体验就很差。但如果用了可中断锁,主线程一调 interrupt(),工作线程立马就能响应,马上停下来。

注意点

不是所有场景都适合用 lockInterruptibly()。如果中断处理逻辑没写好,可能导致锁没释放或者任务状态混乱。记得一定要在 catch 中做好资源清理,尤其是 unlock() 别漏掉。

另外,普通 lock() 方法是不可中断的,只有显式调用 lockInterruptibly() 才具备这个能力。别以为用了 ReentrantLock 就自动有中断支持,得用对方法才行。