多线程-lock与lockInterruptibly的区别及说明

 更新时间:2025年02月27日 10:08:44   作者:九转成圣  
文章主要讨论了Java中ReentrantLock的lock和lockInterruptibly方法的区别,以及AQS中的双向链表设计,lock方法不响应中断,而lockInterruptibly方法会响应中断,AQS的双向链表设计使得线程管理更加高效和灵活,适用于高并发场景

多线程lock与lockInterruptibly区别

在多线程编程中,锁(Lock)是用来确保多个线程在访问共享资源时能够保持一致性和正确性的关键工具。

Java 提供了多种实现锁的机制,其中最常用的是 ReentrantLockReentrantLock 提供了两种获取锁的方法:locklockInterruptibly

这两者在处理线程中断时表现不同,理解这些差异对于编写健壮的多线程程序至关重要。

lock 方法

示例代码:

public static void lock() {
    Lock lock = new ReentrantLock();
    try {
        lock.lock();
        Thread t1 = new Thread(() -> {
            Thread currentThread = Thread.currentThread();
            try {
                lock.lock();
                System.out.println(currentThread.getName() + " lock后的代码");
            } finally {
                lock.unlock();
            }
            System.out.println("currentThread = " + currentThread.getName() + " isInterrupted:" + currentThread.isInterrupted());
        }, "t1");
        t1.start();
        t1.interrupt();
        System.out.println("currentThread = " + Thread.currentThread().getName());
    } finally {
        lock.unlock();
    }
}

执行结果:

currentThread = main
t1 lock后的代码
currentThread = t1 isInterrupted:true

解释:

在这个示例中,主线程(main)首先获取了锁。然后启动一个新线程(t1),该线程尝试再次获取同一个锁。主线程在启动 t1 后立即调用 t1.interrupt() 方法中断 t1 线程。

尽管 t1 线程被中断,它还是成功获取到了锁并执行了锁后的代码。这是因为 lock 方法在获取锁时不会理会线程的中断状态,只要锁可用,它就会获取锁。换句话说,即使线程被中断,它也会继续等待获取锁,直到成功为止。

lockInterruptibly 方法

示例代码:

public static void lockInterruptiblyDemo() {
    Lock lock = new ReentrantLock();
    try {
        lock.lock();
        Thread t1 = new Thread(() -> {
            Thread currentThread = Thread.currentThread();
            try {
                lock.lockInterruptibly();
                System.out.println(currentThread.getName() + " lockInterruptibly后的代码");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            System.out.println("currentThread = " + currentThread.getName() + " isInterrupted:" + currentThread.isInterrupted());
        }, "t1");
        t1.start();
        t1.interrupt();
        System.out.println("currentThread = " + Thread.currentThread().getName());
    } finally {
        lock.unlock();
    }
}

执行结果:

currentThread = main
java.lang.InterruptedException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1220)
    at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
    at Xxx.lambda$lockInterruptiblyDemo$1(Xxx.java:39)
    at java.lang.Thread.run(Thread.java:748)
Exception in thread "t1" java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
    at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
    at Xxx.lambda$lockInterruptiblyDemo$1(Xxx.java:44)
    at java.lang.Thread.run(Thread.java:748)

解释:

在这个示例中,t1 线程尝试获取锁时调用了 lockInterruptibly 方法。与 lock 方法不同,lockInterruptibly 在尝试获取锁时会检查线程的中断状态。如果线程被中断,它会立即抛出 InterruptedException 异常,而不会继续等待获取锁。

执行结果显示,t1 线程在尝试获取锁时被中断,并且抛出了 InterruptedException。由于异常被捕获并打印,t1 线程并没有获取锁,这避免了在某些情况下可能出现的死锁问题。

区别总结

中断响应

  • lock 方法:不响应中断,线程会一直尝试获取锁,直到成功为止。
  • lockInterruptibly 方法:响应中断,线程在检测到中断后会抛出 InterruptedException 并停止尝试获取锁。

使用场景

  • lock 方法适用于不需要考虑中断的场景。
  • lockInterruptibly 方法适用于需要及时响应中断的场景,例如当需要能够中断线程来避免死锁时。

面试题:为什么 AQS 中的队列设计为双向链表?

AQS(AbstractQueuedSynchronizer)是 Java 并发包中锁和同步器的基础框架。AQS 内部使用了一个 FIFO(先进先出)的双向链表来管理等待线程的队列。

原因:

双向遍历

  • 当一个节点(线程)被唤醒时,需要能方便地找到前驱节点以确保线程的唤醒顺序和正确性。
  • 双向链表允许从当前节点轻松访问前驱节点,从而能够修改前驱节点的状态。

节点删除

  • 在一些情况下,节点(线程)可能需要从队列中取消。
  • 双向链表使得删除节点操作更加高效,因为可以直接通过前驱和后继节点来重新链接,而不需要从头遍历。

状态更新

  • 双向链表结构使得更新节点状态(如将节点从等待状态变为运行状态)更为便捷
  • 可以在节点之间高效地传播状态信息

实现复杂性

  • 虽然双向链表在实现上稍微复杂一些
  • 但提供的灵活性和操作效率的提升在高并发场景中是值得的

通过这种设计,AQS 能够更高效地管理线程队列,确保锁和同步器的高性能和正确性。

详细分析:AQS 中的双向链表

AQS 是一个基于节点的框架,所有等待获取锁的线程都会被封装成一个节点并加入到等待队列中。该队列的结构和操作直接影响锁的性能和响应能力。

AQS 中的主要节点结构:

每个节点包含以下主要部分:

  • 前驱节点:指向前一个等待线程的节点。
  • 后继节点:指向后一个等待线程的节点。
  • 线程引用:包含正在等待获取锁的线程的引用。
  • 状态:表示线程的等待状态(如等待、取消等)。

操作示例:

入队

  • 当一个线程请求获取锁但锁不可用时,它会被封装成一个节点并加入到队列末尾。
  • 双向链表结构允许快速定位队尾并将新节点链接上去。

出队

  • 当一个节点被唤醒时(通常是获取到锁),它需要从队列中移除。
  • 双向链表结构允许通过前驱和后继节点高效地重新链接,删除节点。

状态传播

  • 当一个节点状态改变时(如从等待到运行),它需要通知前驱或后继节点。
  • 双向链表允许在节点之间高效地传播状态,确保队列中的每个线程都能正确响应状态变化。

总结

理解 locklockInterruptibly 的区别有助于我们在多线程编程中选择适当的锁获取方式,确保程序的健壮性和响应能力。而 AQS 中的双向链表设计则是确保锁和同步器高效管理等待线程队列的重要基础,这种设计使得线程管理更加高效和灵活,在高并发场景中表现尤为突出。通过深入理解这些底层机制,我们可以更好地编写高性能的多线程应用程序。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java使用HttpClient详细示例

    Java使用HttpClient详细示例

    这篇文章介绍了Java使用HttpClient的详细示例,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • Java中Calendar类的一些常用方法小结

    Java中Calendar类的一些常用方法小结

    项目当中,我们经常会涉及到对时间的处理,Date类最主要的作用就是获得当前时间,同时这个类里面也具有设置时间以及一些其他的功能,但更推荐使用 Calendar 类进行时间和日期的处理,这篇文章主要给大家介绍了关于Java中Calendar类的一些常用方法,需要的朋友可以参考下
    2021-11-11
  • java中httpclient封装post请求和get的请求实例

    java中httpclient封装post请求和get的请求实例

    这篇文章主要介绍了java中httpclient封装post请求和get的请求实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • IDEA中配置Python环境并运行方式

    IDEA中配置Python环境并运行方式

    本文介绍了在Mac和Windows平台上安装Python环境的方法,并详细讲解了如何在IntelliJ IDEA中安装Python插件、创建Python工程和运行Python文件,同时,还提到了一些常用的Python框架,如Django、Google App Engine和SQL支持
    2025-03-03
  • java之Objects.nonNull用法代码解读

    java之Objects.nonNull用法代码解读

    这篇文章主要介绍了java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-03-03
  • Java语言实现简单FTP软件 FTP本地文件管理模块实现(9)

    Java语言实现简单FTP软件 FTP本地文件管理模块实现(9)

    这篇文章主要为大家详细介绍了Java语言实现简单FTP软件,FTP本地文件管理模块的实现方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • idea中创建新类时自动添加注释的实现

    idea中创建新类时自动添加注释的实现

    在每次使用idea创建一个新类时,过了一段时间发现看不懂这个类是用来干嘛的,为了解决这个问题,我们可以设置在创建一个新类时自动添加注释,帮助我们理解这个类的用处,本文主要介绍了在idea中创建新类时自动添加注释的实现,感兴趣的可以了解一下
    2025-03-03
  • Java系统升级与迁移的完整指南

    Java系统升级与迁移的完整指南

    在Java生态中,系统升级和迁移是开发者必须面对的“成人礼”,从JAR地狱到模块化战争,从Java 8到Java 17的版本跳跃,每一次升级都伴随着技术债的清算、架构的重构和性能的飞跃,所以本文给大家介绍了Java系统升级与迁移的完整指南,需要的朋友可以参考下
    2025-08-08
  • Spring Boot Gateway 从入门到精通全面指南

    Spring Boot Gateway 从入门到精通全面指南

    Spring Cloud Gateway 作为微服务架构中的重要组件,提供了强大而灵活的路由与过滤功能,通过本教程,您应该已经了解了如何集成 Spring Cloud Gateway、如何配置路由规则以及如何利用其丰富的功能来满足各种业务需求,感兴趣的朋友跟随小编一起看看吧
    2025-09-09
  • SpringBoot基于数据库实现定时任务过程解析

    SpringBoot基于数据库实现定时任务过程解析

    这篇文章主要介绍了SpringBoot基于数据库实现定时任务过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12

最新评论