ReentrantLock 源码解析

ReentrantLock 源码解析


文章目录

  • ReentrantLock 源码解析
  • 前言
  • 一、字段分析
  • 二、内部类分析
    • 1、Sync
    • 2、FairSync
    • 3、NonfairSync
  • 三、方法分析
    • 1、构造方法
    • 2、其他方法
  • 总结

前言

  • ReentrantLock 实现了 Lock 接口,内部基于 AQS 实现。所以想要弄懂 ReentrantLock ,一定要先弄明白 AQS。

  • ReentrantLock 是可重入锁,同一个线程能够多次获取到同一个 ReentrantLock 锁。

  • ReentrantLock 类采用的是 AQS 技术中的独占模式,实现了 AQS 的 3 个关键方法:tryAcquire(),tryRelease(),isHeldExclusively()方法。

    • 一个线程可以多次通过 ReentrantLock 的 lock()方法获取锁,但是要想完全释放锁,就必须调用相同次数的 unlock()方法。
    • ReentrantLock 对同一个线程的可重入性是通过内部的计数器实现的,同一个线程调用lock()方法加锁,计数器就会 + 1。
    • 同一个线程调用 unlock()方法释放锁,计数器就会 - 1。
  • ReentrantLock 的内部,有两个抽象类:NonFairSync 和 FairSync,二者都继承了 Sync 类,而 Sync 类继承了 AQS 。

    • NonFairSync :表示的是非公平模式。
    • FairSync:表示的是公平模式。
  • ReentrantLock 和 AQS 的关系图:

    在这里插入图片描述

一、字段分析

  • private final Sync sync:ReentrantLock 只有这一个字段,代表了公平锁或者非公平锁,是 AQS 的子类,Sync 是ReentrantLock 内部真正用来操作的类。可以想象 ReentrantLock 只是一个壳,所有关于ReentrantLock 的操作,其实在 ReentrantLock 内部都是对 sync 进行操作的。

二、内部类分析

1、Sync

  • state:继承自 AQS,在 ReentrantLock 中的含义为:
    • 0:当前锁没有被任何线程持有。
    • 1:当一个线程第一次获取该锁时,会尝试使用CAS设置state的值为1。
    • > 1:当一个线程获取锁后,再次调用 lock(),每次调用 state + 1。每次调用 unlock()state - 1,当减为 0 时,该线程释放了锁。
abstract static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = -5179523762034025860L;//抽象的获取锁的方法,由子类实现abstract void lock();//非公平锁的获取锁方法//acquires:传入的是 state 值,传入的是 1final boolean nonfairTryAcquire(int acquires) {//获取当前线程final Thread current = Thread.currentThread();//获取状态值int c = getState();//状态值 = 0 ,说明当前锁没有被任何线程持有if (c == 0) {//使用 CAS 尝试将 state 设为 1if (compareAndSetState(0, acquires)) {//设置成功后,表示获取到了锁,将 锁持有的线程设置为当前线程setExclusiveOwnerThread(current);//加锁成功return true;}}//如果 state = 0,说明已经有线程持有锁了,判断是否是当前线程对象自己持有的else if (current == getExclusiveOwnerThread()) {//当前线程已经持有锁了,将 state + 1int nextc = c + acquires;//state 不停地 + 1,导致溢出了。重入次数过多if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");//设置statesetState(nextc);//可重入获取锁成功return true;}//锁已经被其他线程持有,当前线程获取锁失败return false;}//释放锁//releases :传入的是 state,ReentranLock 释放锁传入的是 1protected final boolean tryRelease(int releases) {//计算释放锁后的state值int c = getState() - releases;//判断占用锁的是不是当前线程,如果不是,则报错if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();//记录是否释放成功    boolean free = false;//如果 state == 0,则释放锁成功if (c == 0) {free = true;//锁释放成功,将锁的持有线程设置为空setExclusiveOwnerThread(null);}//更新状态值setState(c);//返回是否释放成功return free;}//判断持有锁的线程是否为当前线程//true:持有锁的线程是当前线程//false:持有锁的线程不是当前线程protected final boolean isHeldExclusively() {// While we must in general read state before owner,// we don't need to do so to check if current thread is ownerreturn getExclusiveOwnerThread() == Thread.currentThread();}//创建ConditionObject,用来关联条件队列final ConditionObject newCondition() {return new ConditionObject();}//获取持有锁的线程final Thread getOwner() {return getState() == 0 ? null : getExclusiveOwnerThread();}//如果持有锁的线程为当前线程,返回 state,否则返回 0final int getHoldCount() {return isHeldExclusively() ? getState() : 0;}//判断锁是否空闲//true:空闲//false:已被获取final boolean isLocked() {return getState() != 0;}}

2、FairSync

  • 公平锁的实现。
static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;//获取锁final void lock() {acquire(1);}//获取公平锁protected final boolean tryAcquire(int acquires) {//获取当前线程final Thread current = Thread.currentThread();//获取状态值int c = getState();//如果状态值为0,说明锁是空闲的,可以被获取if (c == 0) {//hasQueuedPredecessors():判断当前线程所在的节点(同步队列中)是否还有前驱节点//true:有,false:同步队列为空(还未创建同步队列获只有空的head节点)或当前节点为头结点(指的是head的next节点)//所以翻译为:没有前驱节点 且 使用cas方式设置 state 为 1,说明获取锁成功//因为如果有前驱节点,由于是公平锁,需要先加入到同步队列的队尾进行排队,下一个尝试获取资源的是头结点代表的资源if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {//获取锁资源成功,设置锁拥有的线程为当前线程setExclusiveOwnerThread(current);return true;}}//否则判断锁拥有的线程是否为当前线程,true说明是进行锁的重入操作,将 state + 1else if (current == getExclusiveOwnerThread()) {//计算 state,就是  state + 1int nextc = c + acquires;//判断是否溢出if (nextc < 0)throw new Error("Maximum lock count exceeded");//更新statesetState(nextc);return true;}//否则获取锁失败return false;}}

3、NonfairSync

  • 非公平锁的实现。

    static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;//获取锁final void lock() {//使用cas的方式将state设置为1,成功说明锁未被持有,直接获取锁成功if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());else//失败说明锁已经被持有,使用非公平的方式尝试获取锁acquire(1);}//尝试获取锁资源,采用的是非公平的方式protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}}
    

三、方法分析

1、构造方法

	//无参构造函数,默认使用的是非公平锁public ReentrantLock() {sync = new NonfairSync();}//有参构造函数//true:公平锁//false:非公平锁public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}

2、其他方法

//获取锁,调用的其实是 sync 的方法(忽视中断)
public void lock() {sync.lock();}//获取锁,调用的其实是 sync 的方法(不忽视中断)
public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}//尝试获取锁(非公平锁)
public boolean tryLock() {return sync.nonfairTryAcquire(1);}//尝试获取锁,获取失败后进入 doAcquireNanos 方法,如果时间超过了 timeout,则不进入同步队列了
public boolean tryLock(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(timeout));}//释放锁,调用的其实是 sync 的方法(忽视中断)
public void unlock() {sync.release(1);}//创建 Condition ,其实调用的是sync的方法,创建是 AQS 的 ConditionObject 对象
public Condition newCondition() {return sync.newCondition();}//获取state,如果锁是空闲的则返回 0
public int getHoldCount() {return sync.getHoldCount();}//true:获取到锁的线程为当前线程
//false:获取到锁的线程不为当前线程
public boolean isHeldByCurrentThread() {return sync.isHeldExclusively();}//true:锁已被获取
//false:锁空闲
public boolean isLocked() {return sync.isLocked();}//方法都比较简单
...

总结

  • ReentrantLock 的代码比较简单,更多的实现在 AQS 中,结合 ReentrantLock 和 AQS 的源码,总结下ReentrantLock 获取锁和释放锁的整个流程。

  • 公平模式下获取锁:lock()
    在这里插入图片描述

  • 非公平模式下获取锁:lock()

在这里插入图片描述

  • 非公平模式下释放锁:unlock()和 公平模式下释放锁:unlock()执行方法一样的。

在这里插入图片描述

公平模式和非公平模式下获取锁的区别:体现在 lock 方法和 tryAcquire方法

  • 公平模式下源码:

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

  • 非公平模式下源码:

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

  • 可以发现,非公平锁模式下,如果锁是空闲状态可直接获取锁(lock方法中)。而公平方法中没有。并且在获取锁失败后,调用 tryAcquire 方法时,检查 state == 0 (说明锁是空闲的),非公平模式下是直接尝试获取锁,而公平模式是检查同步队列中是否还有节点,有的话则不获取锁,没有才获取锁。

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

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

相关文章

vue 实现左侧导航栏,右侧锚点定位滚动到指定位置(超简单方法)

项目截图&#xff1a; 实现方法&#xff1a; 点击左侧菜单根据元素id定位到可视内容区域。 浏览器原生提供了一种方法scrollIntoView 。 通过scrollIntoView方法可以把元素滚动到可视区域内。 behavior: "smooth"是指定滚动方式为平滑效果。 具体代码如下&#xf…

使用 PhpMyAdmin 安装 LAMP 服务器

使用 PhpMyAdmin 安装 LAMP 服务器非常简单。按照下面所示的步骤&#xff0c;我们将拥有一个完全可运行的 LAMP 服务器&#xff08;Linux、Apache、MySQL/MariaDB 和 PHP&#xff09;。 什么是 LAMP 服务器&#xff1f; LAMP 代表 Linux、Apache、MySQL 和 PHP。它们共同提供…

智能化安全防护:AI防火墙的原理与应用

随着人工智能技术的迅猛发展&#xff0c;其在各个领域的应用也日益广泛。作为引领数字化转型的重要力量&#xff0c;AI技术为我们的生活和工作带来了前所未有的便利与效率。在通信领域&#xff0c;人工智能的应用同样展现出了巨大的潜力和价值&#xff0c;特别是在网络安全防护…

HTTP/1.1,HTTP/2.0和HTTP/3.0 各版本协议的详解(2024-04-24)

1、HTTP介绍 HTTP 协议有多个版本&#xff0c;目前广泛使用的是 HTTP/1.1 和 HTTP/2&#xff0c;以及正在逐步推广的 HTTP/3。 HTTP/1.1&#xff1a;支持持久连接&#xff0c;允许多个请求/响应通过同一个 TCP 连接传输&#xff0c;减少了建立和关闭连接的消耗。 HTTP/2&#…

基于PaddlePaddle平台训练物体分类——猫狗分类

学习目标&#xff1a; 在百度的PaddlePaddle平台训练自己需要的模型&#xff0c;以训练一个猫狗分类模型为例 PaddlePaddle平台&#xff1a; 飞桨&#xff08;PaddlePaddle&#xff09;是百度开发的深度学习平台&#xff0c;具有动静统一框架、端到端开发套件等特性&#xf…

node.js 解析post请求 方法一

前提&#xff1a;依旧以前面发的node.js服务器动态资源处理代码 具体见 http://t.csdnimg.cn/TSNW9为模板&#xff0c;在这基础上进行修改。与动态资源处理代码不同的是&#xff0c;这次的用户信息我们借用表单来实现。post请求解析来获取和展示用户表单填写信息 1》代码难点&…

【项目实战】基于高并发服务器的搜索引擎

【项目实战】基于高并发服务器的搜索引擎 目录 【项目实战】基于高并发服务器的搜索引擎搜索引擎部分代码index.htmlindex.hpplog.hppparser.cc&#xff08;用于对网页的html文件切分且存储索引关系&#xff09;searcher.hpputil.hpphttp_server.cc&#xff08;用于启动服务器和…

WPForms Pro插件下载:简化您的在线表单构建,提升用户互动

在当今的数字化世界中&#xff0c;表单是网站与用户互动的关键。无论是收集信息、处理订单还是进行调查&#xff0c;一个好的表单可以极大地提升用户体验和转化率。WPForms Pro插件&#xff0c;作为一款专业的WordPress表单构建工具&#xff0c;旨在帮助您轻松创建美观、功能强…

深度学习基础:循环神经网络中的Dropout

深度学习基础&#xff1a;循环神经网络中的Dropout 在深度学习中&#xff0c;过拟合是一个常见的问题&#xff0c;特别是在循环神经网络&#xff08;RNN&#xff09;等复杂模型中。为了应对过拟合问题&#xff0c;研究者们提出了许多方法&#xff0c;其中一种被广泛应用的方法…

TensorFlow进阶一(张量的范数、最值、均值、和函数、张量的比较)

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

短视频评论ID批量爬虫提取获客软件|视频评论下载采集工具

短视频评论批量抓取软件&#xff1a;智能拓客&#xff0c;精准抓取用户反馈 主要功能一览 1. 智能抓取任务创建&#xff1a; 软件提供了任务创建功能&#xff0c;用户只需输入任务名称、搜索关键词以及评论监控词&#xff0c;即可开始智能抓取。不仅能够搜索关键词匹配的视频…

Gradio 最快创建Web 界面部署到服务器并演示机器学习模型,本文提供教学案例以及部署方法,避免使用繁琐的django

最近学习hugging face里面的物体检测模型&#xff0c;发现一个方便快捷的工具&#xff01; Gradio 是通过友好的 Web 界面演示机器学习模型的最快方式&#xff0c;以便任何人都可以在任何地方使用它&#xff01; 一、核心优势&#xff1a; 使用这个开发这种演示机器学习模型的…

就业班 第三阶段(负载均衡) 2401--4.19 day3

二、企业 keepalived 高可用项目实战 1、Keepalived VRRP 介绍 keepalived是什么keepalived是集群管理中保证集群高可用的一个服务软件&#xff0c;用来防止单点故障。 ​ keepalived工作原理keepalived是以VRRP协议为实现基础的&#xff0c;VRRP全称Virtual Router Redundan…

前端开发攻略---封装calendar日历组件,实现日期多选。可根据您的需求任意调整,可玩性强。

1、演示 2、简介 1、该日历组件是纯手搓出来的&#xff0c;没依赖任何组件库&#xff0c;因此您可以随意又轻松的改变代码&#xff0c;以实现您的需求。 2、代码清爽干净&#xff0c;逻辑精妙&#xff0c;您可以好好品尝。 3、好戏开场。 3、代码&#xff08;Vue3写法&#xff…

探索Web3:去中心化的互联网新时代

引言 在过去的几十年里&#xff0c;互联网已经改变了我们的生活方式、商业模式以及社交互动方式。然而&#xff0c;一个新的技术浪潮——Web3正在崭露头角&#xff0c;预示着一个去中心化的互联网新时代的来临。本文将深入探讨Web3技术的定义、特点以及其对未来互联网发展的影…

【数据结构-图】

目录 1 图2 图的定义和基本概念&#xff08;在简单图范围内&#xff09;3 图的类型定义4 图的存储结构4.1 邻接矩阵 表示法4.2 邻接表 表示法4.3 十字链表 表示法4.4 邻接多重表 表示法 5 图的遍历5.1 深度优先搜索-DFS 及 广度优先遍历-BFS 6 图的应用6.1 最小生成树6.1.1 克鲁…

vue cli3开发自己的插件发布到npm

具体流程如下&#xff1a; 1、创建一个vue项目 vue create project 2、编写组件 &#xff08;1&#xff09;新建一个plugins文件夹&#xff08;可自行创建&#xff09; &#xff08;2&#xff09;新建Button组件 &#xff08;3&#xff09;组件挂载&#xff0c;为组件提供 in…

Python绘制3D曲面图

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 探索Python中绘制3D曲面图的艺术 在数据可视化的世界中&#xff0c;3D曲面图是一种强大的工…

数据链路层(计算机网络,待完善)

0、前言 本文大多数图片都来自于 B站UP主&#xff1a;湖科大教书匠 的教学视频&#xff0c;对高军老师及其团队制作出这么优质的课程表示感谢。文章增加了部分个人理解&#xff0c;内容并不是对视频的静态化翻译。 1、概述 1.1、数据链路层在计算机网络体系中的位置 1.2、对…

Jenkins集成Terraform实现阿里云CDN自动刷新

在互联网业务中&#xff0c;CDN的应用已经成了普遍&#xff0c;SRE的日常需求中&#xff0c;CDN的刷新在前端需求逐渐中占了很大比例&#xff0c;并且比较琐碎。做为合格的SRE&#xff0c;把一切自动化是终极使命&#xff0c;而今天就分享通过JenkinsTerraform实现阿里云的CDN自…