ReentrantLock源码解析

ReentrantLock源码解析

文章目录

  • ReentrantLock源码解析
    • 一、ReentrantLock
    • 二、ReentrantLock 的 Sync、FairSync、NonfairSync
      • 2.1 Sync、FairSync、NonfairSync
      • 2.2 NonfairSync 下的 tryAcquire
      • 2.3 FairSync下的 tryAcquire
      • 2.4 tryRelease
    • 三、lock.lock()
      • 3.1 NonfairSync.lock()
      • 3.2 FairSync.lock()
    • 四、lock.unlock()
    • 五、总结

一、ReentrantLock

ReentrantLock 是 Java JUC 中的一个可重入锁ReentrantLock锁 是基于 AQS 实现的。

AQS 中,如果需要使用AQS的特征则需要子类根据使用的场景,重写下面方法:

//查询是否正在独占资源,condition会使用
boolean isHeldExclusively()	
//独占模式,尝试获取资源,成功则返回true,失败则返回false
boolean tryAcquire(int arg)
//独占模式,尝试释放资源,成功则返回true,失败则返回false
boolean tryRelease(int arg)
//共享模式,尝试获取资源,如果返回负数表示失败,否则表示成功。
int tryAcquireShared(int arg)
//共享模式,尝试释放资源,成功则返回true,失败则返回false。
boolean tryReleaseShared(int arg)

由于这里 ReentrantLock 锁的特性,所以下面只需关注独占模式下的几个方法即可。

关于 AQS 中的方法解析可跳转查看 AQS源码解析 这篇文章

下面开始 ReentrantLock 源码的分析:

二、ReentrantLock 的 Sync、FairSync、NonfairSync

2.1 Sync、FairSync、NonfairSync

在声明 ReentrantLock 锁时,有两种方式,一种是无参构造函数,一种则需要指定一个 fair 参数:

  • new ReentrantLock();
  • new ReentrantLock(false);

当使用无参构造函数声明时,则是创建了一个 NonfairSync 对象:
在这里插入图片描述

通过有参的构造函数,则根据传入的 fair 可以选择创建一个 FairSync 对象:
在这里插入图片描述

其实这里也不难理解 NonfairSyncFairSync 其实就是ReentrantLock 锁中的非公平锁和公平锁两种类型。

点到这两个类中,可以看到都继承自 Sync 类:
在这里插入图片描述

Sync 类,则继承了 AQS
在这里插入图片描述

到这里,我们寻找几个关键的方法,在AQS中独占模式下,两大关键的方法是交由子类进行实现的,分别是 tryAcquire() 尝试获取资源,和 tryRelease() 尝试释放资源。

首先来看 tryAcquire() 尝试获取资源:

通过 Sync 类的实现源码发现并没有重写 tryAcquire() 方法,那该方法肯定在下面的子类FairSyncNonfairSync ,分别看下源码确实存在重写的方法:

在这里插入图片描述
在这里插入图片描述

2.2 NonfairSync 下的 tryAcquire

首先看下 NonfairSynctryAcquire() 实现逻辑,可以看到又调用了 nonfairTryAcquire() 就是 Sync 类中的 nonfairTryAcquire() ,从命名上可以分析出就是非公平锁的尝试获取资源,直观就是非公平锁下获取锁操作:
在这里插入图片描述

进入到 Sync 类中的 nonfairTryAcquire()中,可以看到首先获取到 AQS 中的共享资源 state,如果 state 等于 0 ,则将 state 的值修改为 acquires(默认为1,下面会分析到),并设置AQS的独占线程为当前线程,并返回 true ,说白了不就是获取到锁了吗,那就可以理解为 state 等于 0 即是无锁的状态,下面将 state 的值修改为 acquires 就是获取到锁了,改变资源的状态:
在这里插入图片描述

接着如果 state 的值不是 0 ,则当前锁已经被别的线程持有了,这里又判断了下,如果持有锁的线程正好是当前的线程,那不就是锁的重入吗,这种情况下可以直接获得锁,不过这里为了记录重入的次数,对 state 共享资源进行了 + acquires 操作,其实就是 +1 操作。

在这里插入图片描述

如果都没有成功,那此时则获取锁失败,返回 false

2.3 FairSync下的 tryAcquire

FairSync 类下的 tryAcquire() 方法中,和前面 NonfairSync 类似,但不同的是,在获取到锁时,也就是拿到 state 等于 0,进行修改资源时,多了步 hasQueuedPredecessors() 的判断:
在这里插入图片描述

下面可以进到 hasQueuedPredecessors() 的方法中,可以看到是由 AQS 提供的方法,主要就是判断当前节点线程的前面是否还有等待的线程,因为 FairSync 实现的是公平锁的原则,如果当前线程前面还有等待线程,则获取锁资源也轮不到自个,让前面的老大先来:
在这里插入图片描述

hasQueuedPredecessors() 方法理解后,其余的逻辑则和 NonfairSync() 中的一致了。

2.4 tryRelease

到这里已经了解到了tryAcquire() 尝试获取资源的逻辑,上面提到了两个重要方法,还有一个 tryRelease() 没有分析逻辑,还是首先看 Sync 类中是否有重写该方法:

通过源码可以看到,在 Sync 类中就已经对 tryRelease() 进行了重写,而 NonfairSyncFairSync 中都没有重写该方法,那释放资源就是走的 Sync 类下的 tryRelease() 方法:
在这里插入图片描述

在该方法中,可以看到首先还是获取到了 AQS 中的 state 共享资源,然后对该资源进行 - releases (默认releases1,下面会提到)操作,其实就是 -1 操作:
在这里插入图片描述

接着判断了下,如果当前线程不是持有锁线程,就抛出异常,也好理解,没有持有锁的线程跑过来释放锁,那肯定有问题了呀。

接着再进行判断 state 是不是等于 0 ,上面讲到在锁重入的情况下,记录重入的次数是对 state 进行 +1 操作,而这边又对 state 进行 -1 操作,如果减到最后 state 有成了最初的 0 ,那不就是重入的锁和当前持有的锁都释放完了吗,这个时候就可以将持有锁的线程置为空了,并修改最新的 state

在这里插入图片描述

看到这里就会发现获取锁和释放锁,无非就是对 AQS 中的共享资源进行操作。理解了这两大核心的方法后,下面就可以看如何运用在 ReentrantLock 中的了。

三、lock.lock()

ReentrantLock 中,需要获取锁时,直接使用 lock.lock() 即可,那 lock.lock() 到底做了什么呢,点到该方法中,可以看到是调用的 Synclock() 方法,而 Sync 中的 lock() 方法是抽象方法,具体实现肯定在子类的 NonfairSyncFairSync 中。

在这里插入图片描述

3.1 NonfairSync.lock()

首先点进 NonfairSync 非公平锁中的 lock() 方法,直接进行了将 AQS 中的共享资源 state0 改为 1 ,如果修改成功,根据上面分析的结论不就是获取锁成功了吗,可以将 AQS中的独占线程设为自己了。但是如果其他线程修改成功了,这里使用 CAS 就会修改失败,因此就会进到 acquire() 方法,注意这里传递的参数默认就是 1 ,对应着前面括号中的说明:
在这里插入图片描述

acquire() 方法,就是 AQS 中的独占模式获取同步资源的逻辑,会调用当前方法的 tryAcquire() 尝试获取资源,如果获取不到,则加入到 AQS 的阻塞队列并阻塞挂起线程。
在这里插入图片描述

关于 acquire 方法的源码解析可查看 AQS源码解析 。

3.2 FairSync.lock()

FairSync 公平锁中,由于需要遵循先进先出的原则,这里没有直接已粗暴的形式对 state 进行修改,而是直接调用了 AQS 中的 acquire() 方法,而 acquire() 方法又会调用当前类的 tryAcquire() 获取资源。

但在当前类的 tryAcquire() 方法中,如果获取到了资源,会接着进行判断当前线程的前面是否还有等待的线程,如果有则让出来让别人获取资源,因此就遵循了公平锁的原则,注意这里传递的参数默认就是 1 ,同样对应着前面括号中的说明:
在这里插入图片描述

同样 tryAcquire() 尝试获取资源,如果获取不到,则加入到 AQS 的阻塞队列并阻塞挂起线程。

四、lock.unlock()

上面了解到了 lock() 的逻辑,既然上锁了肯定需要解锁,下面点到 unlock() 方法中,可以看到直接使用了 Syncrelease() 方法释放资源,其实是 AQS 中的 release() 方法,注意这里传递的参数默认就是 1,同样对应着前面括号中的说明:
在这里插入图片描述

AQSrelease() 方法中,首先会调用 Sync 类的 tryRelease() 释放资源,然后对已阻塞的线程进行唤醒:
在这里插入图片描述

关于 release() 方法的源码解析可查看 AQS源码解析 。

五、总结

通过阅读 ReentrantLock 的源码可以发现,大量依赖于 AQS 中提供的方法,所以在阅读前一定要理解下 AQS 的作用和功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/212289.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言——结构体(全)

目录 一、结构体的设计 二、结构体变量的初始化 2.1结构体在内存表示; 2.2结构体初始化; 2.3结构体指针变量 2.4结构体嵌套结构体 三、结构体成员访问 3.1、结构体成员访问 3.2、结构体变量和指针 ​3.3、结构体和函数 四、结构体与数组 五、…

汇编-PROC定义子过程(函数)

过程定义 过程用PROC和ENDP伪指令来声明, 并且必须为其分配一个名字(有效的标识符) 。目前为止, 我们所有编写的程序都包含了一个main过程, 例如: 当要创建的过程不是程序的启动过程时, 就用RET指令来结束它。RET强制…

好细的Vue安装与配置

一、下载和安装Vue 官网下载地址Download | Node.js 选择适合自己的版本,推荐LTS,长久稳定版本。 我这里选择的是Windows Installer(.msi) 64-bit 下载好后,双击下载的安装包。 点next 勾选I accept............,点next 这里建…

MATLAB - text的两种使用方法

text小技巧 1. 常规使用(Method 1)2. 在显示画面的相对位置(Method 2)3. 举个例子 1. 常规使用(Method 1) text(x,y,txt)2. 在显示画面的相对位置(Method 2) text(string,‘ABC’,…

使用端口扫描工具解决开放端口威胁并增强安全性

从暴露网络漏洞到成为入侵者的通道,开放端口可能会带来多种风险向量,威胁到网络的机密性、完整性和可用性。因此,最佳做法是关闭打开的端口,为了应对开放端口带来的风险,网络管理员依靠端口扫描工具来识别、检查、分析…

跨境电商必须要海外代理IP吗?盘点五大海外代理IP

相信跨境电商人近日都为了2023的跨境黑五旺季奋战,而2024也即将来临,对于跨境人的考验一波接着一波,根据Adobe Analytics的数据,2022年黑色星期五的销售额创下91.2亿美元新高,网络星期的销售额同样达到创纪录的113亿美…

jenkins 参数构建

应用保存 [rootjenkins-node1 .ssh]# ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved i…

ubuntu搭建phpmyadmin+wordpress

Ubuntu搭建phpmyadmin wordpress Linux系统设置:Ubuntu 22配置apache2搭建phpmyadmin配置Nginx环境,搭建wordpress Linux系统设置:Ubuntu 22 配置apache2 安装apache2 sudo apt -y install apache2设置端口号为8080 sudo vim /etc/apache…

Java中23种设计模式

一、创建型模式 1.单例模式(Singleton Pattern) 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提…

leetcode刷题之用栈实现队列(C语言版)

leetcode刷题之用栈实现队列(C语言版) 一、题目描述二、题目要求三、题目解析Ⅰ、typedef structⅡ、MyQueue* myQueueCreateⅢ、void myQueuePush(MyQueue* obj, int x)Ⅳ、int myQueuePeek(MyQueue* obj)Ⅴ、int myQueuePop(MyQueue* obj)Ⅶ、bool myQ…

微服务保护 Sentinel

1.初识Sentinel 文章目录 1.初识Sentinel1.1.雪崩问题及解决方案1.1.1.雪崩问题1.1.2.超时处理1.1.3.仓壁模式1.1.4.断路器1.1.5.限流1.1.6.总结 1.2.服务保护技术对比1.3.Sentinel介绍和安装1.3.1.初识Sentinel1.3.2.安装Sentinel 1.4.微服务整合Sentinel 2.流量控制2.1.簇点链…

【Vue】创建第一个实例

步骤&#xff1a; 1.创建容器 2.引包 3.创建实例 4.添加配置项 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body><!--准备容器 --> <di…