Java并发编程Lock Condition和ReentrantLock基本原理

 更新时间:2023年09月15日 11:41:31   作者:福  
这篇文章主要介绍了Java并发编程Lock Condition和ReentrantLock基本原理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

Lock框架

Lock框架为java并发编程提供了除synchronized之外的另外一种选择。synchronized是隐式实现,底层封装了对锁资源的获取和释放的所有实现细节,程序员不需要关心也没有办法关心这些细节,使用起来非常方便也非常安全。

而Lock由java语言实现,公开了锁资源获取和释放的所有细节,在资源锁定过程中提供了更多选项,在获取锁资源后,可以通过Condition对象对锁资源做细粒度的管理。

最关键的是Lock大量使用了CAS,充分利用“持有锁的线程不会长时间占用锁”这一假设,有可能的情况下就尽量先自旋、后锁定资源。所以多线程环境下Lock应该比synchronized有更好的性能。

java线程池框架Executor中大量使用了基于Lock接口的ReentrantLock,掌握ReentrantLock是深入理解各种Executor(ThreadPoolExecutor、ScheduledThreadPoolExecutor等)以及各种阻塞队列的必要前提。

Lock有独占锁、共享锁的区别,独占锁是指某一线程获取锁资源后即独占该锁资源、其他线程只能等待,共享锁是指多个线程能同时获得锁资源。

今天我们的研究对象是ReentrantLock,ReentrantLock是独占锁,主要研究内容:

  • ReentrantLock的基本概念
  • 基础数据机构:AQS,CLH队列
  • 公平锁、非公平锁
  • Condition
  • 没有Condition参与的lock、unlock
  • 有Condition参与的lock、unlock

ReentrantLock的基本概念

顾名思义,ReentrantLock是“可重入锁”,意思是同一线程可以多次获得锁,n次获得需要n次释放才能最终释放掉ReentrantLock。

ReentrantLock的基本原理:

  • 与synchronized不同,ReentrantLock不存在“锁对象”的概念,或者可以理解为锁对象就是ReentrantLock对象本身
  • ReentrantLock设置一个状态值,通过对状态值的原子操作实现对锁资源的获取和释放,任何一个线程能获取锁资源的充分必要条件是ReentrantLock处于空闲状态,同理,任何一个线程获得锁资源后ReentrantLock即处于占用状态
  • ReentrantLock的两个最基本的操作:lock和unlock,lock获取锁资源,unlock释放锁资源
  • ReentrantLock维护一个CLH队列,CLH队列是一个先进先出的双向队列
  • ReentrantLock处于空闲状态则lock调用立即返回,调用线程获得锁资源。否则,请求线程进入CLH队列排队,等待被其他线程唤醒
  • 获得锁资源的线程在业务执行完成后调用unlock释放锁资源,之后以FIFO的原则唤醒最先进入队列排队的线程
  • 被唤醒的线程继续执行lock操作,节点从CLH队列出队,返回---意味着请求锁资源的线程在等待后获取锁资源成功,继续第6步的逻辑

以上是没有Condition对象参与的ReentrantLock的获取、释放锁资源的逻辑,相对比较简单。

有Condition参与的时候,情况会稍微复杂一点:

  • ReentrantLock对象可以通过new Condition()操作持有Condition对象,一个ReentrantLock可以持有多个Condition对象
  • Condition维护一个Condition队列
  • Condition的常用的操作包括await、signal等,执行操作的时候假设当前线程已经获取到了ReentrantLock锁资源
  • await操作会释放掉当前线程已经获取到的ReentrantLock锁资源、挂起当前线程,并且将当前线程加入Condition的队列排队等待被其他线程唤醒。比如DelayedWorkQueue的take方法中,如果当前DelayedWorkQueue队列空的话,则take线程加入到命名为available的Condition中排队等候
  • 当相关操作可能导致Condition的条件满足的时候,调用Condition的signal方法唤醒在Condition队列中等待的线程。比如上例中DelayedWorkQueue的add方法完成之后,调用available的signal方法,唤醒在available队列中排队等候的线程。
  • 线程被唤醒之后从Condition队列出队,进ReentrantLock的CLH队列排队等待重新获取锁资源

Condition举例:take方法中队列空的话,挂起等待

Condition举例:offer方法中写入队列后,唤醒等待的线程

对ReentrantLock应该有一个基本的认识了,如果只是想要对ReentrantLock做一个基本了解、能够看懂ReentrantLock的应用、而不是要从源码角度做深入研究的话,个人认为掌握上面这些基本原理应该就够了,保证能看懂阻塞队列、线程池中的有关ReentrantLock的源码逻辑了。

但是如果想要彻底搞清楚ReentrantLock到底是怎么实现以上逻辑的,就需要从源码角度继续做深入研究了。

ReentrantLock数据结构:AQS及CLH队列

多个线程同时竞争ReentrantLock锁资源的时候,只能有一个竞争获胜的线程获得锁资源、其他线程就只能排队等待。这个用来排队的队列就是CLH队列,AQS(AbstractQueuedSynchronizer)是实现CLH队列的虚拟类。

ReentrantLock有一个非常重要的属性Sync,Sync是AQS的虚拟扩展类,Sync有两个实现类:NonfairSync和FairSync,类结构如下:

NonfairSync和FairSync都是AQS的最终实现,AQS虚拟类是一个标准模板,定义了Lock锁的基本数据结构(阻塞队列)、并实现了Lock的绝大部分功能。

进入队列排队的线程被封装为Node,Node是AQS定义的内部类,是我们学习AQS首先要掌握的内容。

Node的重要属性:

waitStatus:等待状态,Node就是用来排队的,waitStatus就代表当前节点的等待状态,有以下几种等待状态:

  • CANCELLED = 1:表示当前等待线程已经被calcel掉了
  • SIGNAL = -1:表示该节点是在CLH队列中排队等待出队
  • CONDITION = -2:表示当前节点是在Condition队列中等待出队
  • PROPAGATE = -3:共享锁会用到,暂不分析

prev:上一节点
next:双向队列嘛,当然也要有下一节点
Thread thread:节点的主角,排队线程
nextWaiter:Condition队列专用,用来指向Condition队列的下一节点

AQS的同步队列(CLH)以及Condition队列的节点都是用这个Node,所以Node类做了一部分针对两者的兼容设计,比如nextWaiter是针对Condtion队列的下一节点,next是针对CLH的下一节点。

AQS重要属性

state:锁状态,通过对state的原子操作实现对锁资源的控制:某一线程通过原子操作成功将state从空闲修改为占用则意味着当前线程成功获得了锁资源。无法获得锁资源的线程则封装为Node节点进入队列排队等待。

head:首节点,头节点
tail:尾结点

通过head节点、tail节点,以及每个节点的prev、next,AQS实现了一个双向队列。

公平锁和非公平锁

所谓的公平锁和非公平锁就是由Sync属性决定的:当Sync创建为NonfairSync的时候,就是非公平的ReentrantLock,否则就是公平的ReentrantLock。

使用无参构造器创建的是非公平ReentrantLock,有参构造器ReentrantLock(boolean fair)可以通过参数指定创建公平还是非公平锁。

公平锁在线程请求锁资源的时候会检查CLH队列,队列不空的话首先进入队列排队,先提出申请的线程会优先获得锁资源,因此是“公平”的锁。

非公平锁在线程请求锁资源的时候不会检查CLH队列,直接尝试获得锁资源,获取失败后才进入队列排队。所以请求线程会得到比队列中的线程更高的优先级,对于队列中排队的线程来说是不公平的,所以叫非公平锁。

Condition

Condition提供await和signal(以及他们的变种)方法为ReentrantLock锁资源提供更多选择:当前线程获取到ReentrantLock锁资源后,可以通过Condition对象的await方法挂起当前线程直到其他线程通过该对象的signal方法唤醒。

一个ReentrantLock可以创建多个Condition对象,每一个Condition对象都是独立的、互不影响。ReentrantLock好比是一条街上的黑社会老大,黑社会老大首先要把这条街拿下,也就是获得ReentrantLock锁资源。之后的每一个Condition好比是这条街道上的饭店A、小卖店B、公共卫生间C,分别对应ConditionObjectA、ConditionObjectB、ConditionObjectC,得到黑社会老大允许后你就可以随意进出饭店吃饭了,但是如果饭店客满了,就必须通过调用ConditionObjectA的await方法进入到ConditionObjectA的队列中排队等待(当前线程封装为AQS中的Node进入队列(假设叫NodeA),当前线程A挂起),此时黑社会老大需要交出对整条街的锁权限(貌似不太合理...),此后饭店A有人吃完了要离店,就会通过ConditionObjectA的signal方法通知正在队列中排队等候的NodeA,于是NodeA从ConditionObjectA队列中出来,到ReentrantLock的CLH队列中排队、等待重新获取ReentrantLock锁资源之后再唤醒线程A。这个过程中如果有其他人(其他线程)要进入小卖店B,需要进行操作的就是小卖店对应的ConditionObjectB,和饭店对应的ConditionObjectA没有任何关系。

小结

发现开篇定下的内容太多了,篇幅所限,后面的“没有Condition参与的lock、unlock”以及“有Condition参与的lock、unlock”,基本就是上述逻辑的源码分析,放在下一篇。

以上就是Java并发编程Lock Condition和ReentrantLock基本原理的详细内容,更多关于Java并发编程的资料请关注脚本之家其它相关文章!

相关文章

  • 基于Java子线程中的异常处理方法(通用)

    基于Java子线程中的异常处理方法(通用)

    下面小编就为大家带来一篇基于Java子线程中的异常处理方法(通用)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • java synchronized实现可见性过程解析

    java synchronized实现可见性过程解析

    这篇文章主要介绍了java synchronized实现可见性过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • Java实体类之间的相互转换方式

    Java实体类之间的相互转换方式

    这篇文章主要介绍了Java实体类之间的相互转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • SpringBoot开发技巧启动时配置校验实现示例

    SpringBoot开发技巧启动时配置校验实现示例

    这篇文章主要为大家介绍了SpringBoot开发在启动时自动配置校验的实现示例及原理解析,有需要的朋友可以借鉴参考下希望能够有所帮助
    2021-10-10
  • 浅谈Java中static和非static的区别

    浅谈Java中static和非static的区别

    这篇文章主要介绍了Java中static和非static的相关内容,小编觉得还是很不错的,这里分享给大家,需要的朋友可以参考下。
    2017-10-10
  • SpringMVC返回图片的几种方式(小结)

    SpringMVC返回图片的几种方式(小结)

    这篇文章主要介绍了SpringMVC返回图片的几种方式(小结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • SpringBoot集成ElasticSearch(ES)实现全文搜索功能

    SpringBoot集成ElasticSearch(ES)实现全文搜索功能

    Elasticsearch是一个开源的分布式搜索和分析引擎,它被设计用于处理大规模数据集,它提供了一个分布式多用户能力的全文搜索引擎,本文将给大家介绍SpringBoot集成ElasticSearch(ES)实现全文搜索功能,需要的朋友可以参考下
    2024-02-02
  • Jenkins自动化部署SpringBoot项目的实现

    Jenkins自动化部署SpringBoot项目的实现

    本文主要介绍了Jenkins自动化部署SpringBoot项目的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2023-01-01
  • Spring与MyBatis集成 AOP整合PageHelper插件的操作过程

    Spring与MyBatis集成 AOP整合PageHelper插件的操作过程

    Spring与MyBatis集成的主要目的是为了提供更强大的数据访问和事务管理能力,以及简化配置和提高开发效率,这篇文章主要介绍了Spring与MyBatis集成AOP整合PageHelper插件,需要的朋友可以参考下
    2023-08-08
  • SpringBoot中对SpringMVC的自动配置详解

    SpringBoot中对SpringMVC的自动配置详解

    这篇文章主要介绍了SpringBoot中的SpringMVC自动配置详解,Spring MVC自动配置是Spring Boot提供的一种特性,它可以自动配置Spring MVC的相关组件,简化了开发人员的配置工作,需要的朋友可以参考下
    2023-10-10

最新评论