Java 并发编程之深入理解"锁可中断"机制

 更新时间:2026年03月18日 09:35:07   作者:亲爱的非洲野猪  
在Java并发编程中,死锁(Deadlock)和线程阻塞(Blocking)是开发者最头疼的问题之一,本文给大家介绍Java 并发编程之深入理解“锁可中断”机制,感兴趣的朋友跟随小编一起看看吧

在 Java 并发编程中,死锁(Deadlock)和线程阻塞(Blocking)是开发者最头疼的问题之一。当一个线程无限期地等待一个锁时,整个系统可能会陷入停滞。

为了解决这个问题,Java 提供了 java.util.concurrent.locks.Lock 接口,其中有一个关键方法:lockInterruptibly()。它实现了 “锁可中断” 的特性。

1. 什么是“锁可中断”?

“锁可中断” 指的是:当一个线程在等待获取锁的过程中,如果收到了中断信号(interrupt),它可以放弃等待,抛出 InterruptedException 异常,从而结束阻塞状态,而不是无限期地傻等下去。

这是 ReentrantLock 等显式锁相对于内置锁 synchronized 的一个重大优势,它赋予了开发者主动控制线程等待行为的能力。

核心对比

特性synchronizedReentrantLock.lock()ReentrantLock.lockInterruptibly()
锁类型内置锁 (隐式)显式锁显式锁
等待锁时响应中断❌ 不支持❌ 不支持✅ 支持
行为描述线程会一直死等,忽略中断信号,直到拿到锁。同 synchronized,一直死等。收到中断信号后,停止等待,抛出异常。
灵活性

2. 代码实战:等待锁时的中断

下面是一个演示“锁可中断”的经典场景。线程 A 持有锁,线程 B 尝试获取锁。主线程随后中断线程 B。

import java.util.concurrent.locks.ReentrantLock;
public class InterruptibleLockDemo {
    private static final ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        // 1. 线程 A 获取锁,并长时间持有
        Thread threadA = new Thread(() -> {
            lock.lock();
            try {
                System.out.println("Thread A: 获取了锁,开始执行长任务...");
                Thread.sleep(100000); // 模拟长时间占用
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
        // 2. 线程 B 尝试获取锁,使用 lockInterruptibly()
        Thread threadB = new Thread(() -> {
            try {
                System.out.println("Thread B: 尝试获取锁...");
                // 关键点:使用可中断的获取锁方法
                lock.lockInterruptibly(); 
                try {
                    System.out.println("Thread B: 成功获取锁!");
                } finally {
                    lock.unlock();
                }
            } catch (InterruptedException e) {
                // 3. 捕获中断异常
                System.out.println("Thread B: 等待锁时被中断了!" + e.getMessage());
            }
        });
        threadA.start();
        Thread.sleep(1000); // 确保 A 先拿到锁
        threadB.start();
        Thread.sleep(1000); // 确保 B 进入等待状态
        // 4. 主线程中断线程 B
        System.out.println("Main: 准备中断 Thread B...");
        threadB.interrupt();
    }
}

输出结果:

Thread A: 获取了锁,开始执行长任务...
Thread B: 尝试获取锁...
Main: 准备中断 Thread B...
Thread B: 等待锁时被中断了!java.lang.InterruptedException

结论: 线程 B 没有死等线程 A 释放锁,而是响应了中断信号,提前退出了等待。

3. 核心误区:持有锁时能被中断吗?

这是很多开发者容易混淆的地方。“锁可中断”仅针对“等待获取锁”的阶段,而不是“已经持有锁”的阶段。

场景分析

  • 等待锁时(Waiting for Lock):
    • 调用 lockInterruptibly() 后,锁被占用,线程阻塞。
    • 此时 interrupt() -> 线程立即醒来,抛出异常,放弃获取锁。
  • 持有锁时(Holding Lock):
    • 线程已经拿到了锁,正在执行临界区代码。
  • 此时 interrupt() -> 线程的中断标志位变为 true,但线程不会停止,锁也不会自动释放。
  • 线程会继续执行,直到代码自然结束或遇到其他可中断阻塞(如 sleep)。

为什么持有锁时不强制释放?

这是为了数据安全

假设线程 A 持有锁,正在执行一个多步操作(如:读取余额 -> 计算利息 -> 写入余额)。如果在中途强制中断并释放锁:

  • 线程 A 可能只执行了“读取”,还没“写入”。
  • 锁被释放,线程 B 介入,读到了不一致的中间状态数据。
  • 导致数据脏读或逻辑错误。

因此,Java 的设计原则是:中断只是“建议”线程停止,持有锁的线程必须自己决定何时安全地退出,并在 finally 块中手动释放锁。

代码验证:持有锁时无视中断

// 简化的逻辑演示
Thread worker = new Thread(() -> {
    lock.lock(); // 获取锁
    try {
        // 执行任务,即使此时被 interrupt,也会继续执行
        for (int i = 0; i < 3; i++) {
            if (Thread.interrupted()) {
                System.out.println("Worker: 发现中断标志,但我持有锁,继续执行...");
            }
            System.out.println("Worker: 执行步骤 " + i);
        }
    } finally {
        lock.unlock(); // 必须手动释放
        System.out.println("Worker: 锁已释放。");
    }
});

4. 应用场景

既然 synchronized 更简单,为什么还要用可中断锁?主要适用于以下场景:

  • 避免死锁(Deadlock Avoidance):
    • 如果系统检测到死锁风险,可以通过中断其中一个等待锁的线程,让它回退并释放已持有的资源,从而打破死锁循环。
  • 任务取消(Task Cancellation):
    • 用户在前端点击了“取消”按钮,后端需要停止正在排队的任务。如果任务在等待锁,可中断锁允许任务立即响应取消请求,释放线程资源,提升系统响应速度。
  • 灵活的超时控制:

虽然 tryLock(timeout) 也可以避免无限等待,但 lockInterruptibly() 提供了更灵活的被动响应机制(由外部监控线程决定何时停止,而不是固定时间)。

5. 最佳实践与注意事项

在使用 lockInterruptibly() 时,必须遵循严格的编码规范,否则可能导致死锁或异常。

5.1 正确的解锁姿势

如果 lockInterruptibly() 抛出了 InterruptedException,说明锁没有获取成功。此时不能调用 unlock(),否则会抛出 IllegalMonitorStateException

推荐的标准写法:

boolean locked = false;
try {
    lock.lockInterruptibly();
    locked = true; // 标记锁获取成功
    // --- 业务逻辑 ---
} catch (InterruptedException e) {
    // 处理中断
    Thread.currentThread().interrupt(); // 恢复中断状态,不要吞掉中断
} finally {
    if (locked) {
        lock.unlock(); // 只有获取成功才解锁
    }
}

5.2 不要吞掉中断信号

catch (InterruptedException e) 块中,除非你打算立即结束线程,否则最好恢复中断状态:
Thread.currentThread().interrupt();
这样上层调用者才能知道线程被中断过,以便做进一步处理。

6. 总结

  • 锁可中断ReentrantLock 提供的高级特性,通过 lockInterruptibly() 实现。
  • 它允许线程在等待锁的过程中响应中断信号,避免无限期阻塞。
  • 不会强制释放已经持有的锁,以保证数据一致性。
  • 使用时需注意 try-finally 的正确写法,避免在未获取锁时调用 unlock()

掌握“锁可中断”机制,能让你在面对复杂的并发场景(如死锁恢复、任务取消)时,拥有更多的控制权和灵活性,是编写高健壮性 Java 并发程序的必备技能。

到此这篇关于Java 并发编程之深入理解“锁可中断”机制的文章就介绍到这了,更多相关java锁可中断内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Druid基本配置及内置监控使用_动力节点Java学院整理

    Druid基本配置及内置监控使用_动力节点Java学院整理

    这篇文章主要介绍了Druid基本配置及内置监控使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • Java集合快速失败与安全失败解析

    Java集合快速失败与安全失败解析

    这篇文章主要介绍了Java集合快速失败与安全失败解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Spring如何自定义XML配置扩展

    Spring如何自定义XML配置扩展

    这篇文章主要介绍了Spring如何自定义XML配置扩展,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • JAVA数据写入生成excel文件和发送邮件

    JAVA数据写入生成excel文件和发送邮件

    这篇文章主要介绍了JAVA数据写入生成excel文件和发送邮件,流程:先导包 => 邮箱开启配置 => java写好配置类 => 测试发送 => 数据写入excel => 邮件带附件发送
    2024-06-06
  • Java基础 Servlet监听器详解

    Java基础 Servlet监听器详解

    这篇文章主要介绍了Java基础 Servlet监听器详解的相关资料,需要的朋友可以参考下
    2017-07-07
  • spring中FactoryBean中的getObject()方法实例解析

    spring中FactoryBean中的getObject()方法实例解析

    这篇文章主要介绍了spring中FactoryBean中的getObject()方法实例解析,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-02-02
  • 关于Spring的统一功能处理(拦截器)实现

    关于Spring的统一功能处理(拦截器)实现

    这篇文章主要介绍了关于Spring的统一功能处理(拦截器)实现,每个方法中都要单独写用户登录验证的方法,即使封装成公共方法,也一样要传参调用和在方法中进行判断,需要的朋友可以参考下
    2023-05-05
  • MyBatis自定义映射关系和关联查询实现方法详解

    MyBatis自定义映射关系和关联查询实现方法详解

    这篇文章主要介绍了MyBatis自定义映射关系和关联查询实现方法,当POJO属性名与数据库列名不一致时,需要自定义实体类和结果集的映射关系,在MyBatis注解开发中,使用@Results定义并使用自定义映射,使用 @ResultMap使用自定义映射
    2023-04-04
  • IDEA的常见的设置和优化功能图文详解

    IDEA的常见的设置和优化功能图文详解

    这篇文章主要介绍了IDEA的常见的设置和优化功能,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • java 多线程的同步几种方法

    java 多线程的同步几种方法

    这篇文章主要介绍了java 多线程的同步几种方法的相关资料,这里提供5种方法,需要的朋友可以参考下
    2017-09-09

最新评论