独占锁ReentrantLock的原理

类图结构

ReentrantLock是可重入的独占锁,同时只能有一个线程可以获取该锁,其他获取该锁的线程会被阻塞而被放入该锁的AQS阻塞队列里面。

首先看下ReentrantLock的类图以便对它的实现有个大致了解。

在这里插入图片描述
从类图可以看到,ReentrantLock最终还是使用AQS来实现的,并且根据参数来决定其内部是一个公平还是非公平锁,默认是非公平锁。

在这里插入图片描述
其中Sync类直接继承自AQS,它的子类NonfairSync和FairSync分别实现了获取锁的非公平与公平策略。

在这里,AQS的state状态值表示线程获取该锁的可重入次数,在默认情况下,state的值为0表示当前锁没有被任何线程持有。

当一个线程第一次获取该锁时会尝试使用CAS设置state的值为1,如果CAS成功则当前线程获取了该锁,然后记录该锁的持有者为当前线程。

在该线程没有释放锁的情况下第二次获取该锁后,状态值被设置为2,这就是可重入次数。

在该线程释放该锁时,会尝试使用CAS让状态值减1,如果减1后状态值为0,则当前线程释放该锁。

获取锁

void lock()方法

当一个线程调用该方法时,说明该线程希望获取该锁。

如果锁当前没有被其他线程占用并且当前线程之前没有获取过该锁,则当前线程会获取到该锁,然后设置当前锁的拥有者为当前线程,并设置AQS的状态值为1,然后直接返回。

如果当前线程之前已经获取过该锁,则这次只是简单地把AQS的状态值加1后返回。

如果该锁已经被其他线程持有,则调用该方法的线程会被放入AQS队列后阻塞挂起。

在这里插入图片描述
在如上代码中,ReentrantLock的lock()委托给了sync类,根据创建ReentrantLock构造函数选择sync的实现是NonfairSync还是FairSync,这个锁是一个非公平锁或者公平锁。

这里先看sync的子类NonfairSync的情况,也就是非公平锁时。

在这里插入图片描述
在代码(1)中,因为默认AQS的状态值为0,所以第一个调用Lock的线程会通过CAS设置状态值为1,CAS成功则表示当前线程获取到了锁,然后setExclusiveOwnerThread设置该锁持有者是当前线程。

如果这时候有其他线程调用lock方法企图获取该锁,CAS会失败,然后会调用AQS的acquire方法。注意,传递参数为1,这里再贴下AQS的acquire的核心代码。

在这里插入图片描述
之前说过,AQS并没有提供可用的tryAcquire方法,tryAcquire方法需要子类自己定制化,所以这里代码(3)会调用ReentrantLock重写的tryAcquire方法。我们先看下非公平锁的代码。

在这里插入图片描述
首先代码(4)会查看当前锁的状态值是否为0,为0则说明当前该锁空闲,那么就尝试CAS获取该锁,将AQS的状态值从0设置为1,并设置当前锁的持有者为当前线程然后返回,true。

如果当前状态值不为0则说明该锁已经被某个线程持有,所以代码(5)查看当前线程是否是该锁的持有者,如果当前线程是该锁的持有者,则状态值加1,然后返回true,这里需要注意,nextc<0说明可重入次数溢出了。

如果当前线程不是锁的持有者则返回false,然后其会被放入AQS阻塞队列。

介绍完了非公平锁的实现代码,回过头来看看非公平在这里是怎么体现的。

首先非公平是说先尝试获取锁的线程并不一定比后尝试获取锁的线程优先获取锁。

这里假设线程A调用lock()方法时执行到nonfairTryAcquire的代码(4),发现当前状态值不为0,所以执行代码(5),发现当前线程不是线程持有者,则执行代码(6)返回false,然后当前线程被放入AQS阻塞队列。

这时候线程B也调用了lock()方法执行到nonfairTryAcquire的代码(4),发现当前状态值为0了(假设占有该锁的其他线程释放了该锁),所以通过CAS设置获取到了该锁。明明是线程A先请求获取该锁呀,这就是非公平的体现。

这里线程B在获取锁前并没有查看当前AQS队列里面是否有比自己更早请求该锁的线程,而是使用了抢夺策略。

那么下面看看公平锁是怎么实现公平的。公平锁的话只需要看FairSync重写的tryAcquire方法。

在这里插入图片描述
如以上代码所示,公平的tryAcquire策略与非公平的类似,不同之处在于,代码(8)在设置CAS前添加了hasQueuedPredecessors方法,该方法是实现公平性的核心代码,代码如下。

在这里插入图片描述
在如上代码中,如果当前线程节点有前驱节点则返回true,否则如果当前AQS队列为空或者当前线程节点是AQS的第一个节点则返回false。

其中如果ht则说明当前队列为空,直接返回false。
如果h!=t并且s
null则说明有一个元素将要作为AQS的第一个节点入队列(回顾前面的内容,enq函数的第一个元素入队列是两步操作:首先创建一个哨兵头节点,然后将第一个元素插入哨兵节点后面),那么返回true,如果h!=t并且s!=null和s.thread !=Thread.currentThread()则说明队列里面的第一个元素不是当前线程,那么返回true。

void locklnterruptibly()方法

该方法与lock()方法类似,它的不同在于,它对中断进行响应,就是当前线程在调用该方法时,如果其他线程调用了当前线程的interrupt()方法,则当前线程会抛出InterruptedException异常,然后返回。

在这里插入图片描述

boolean tryLock()方法

尝试获取锁,如果当前该锁没有被其他线程持有,则当前线程获取该锁并返回true,否则返回false。注意,该方法不会引起当前线程阻塞。

在这里插入图片描述如上代码与非公平锁的tryAcquire()方法代码类似,所以tryLock()使用的是非公平策略。

boolean tryLock(long timeout,TimeUnit unit)

方法尝试获取锁,与tryLock()的不同之处在于,它设置了超时时间,如果超时时间到没有获取到该锁则返回false。

在这里插入图片描述

释放锁

void unlock()方法

尝试释放锁,如果当前线程持有该锁,则调用该方法会让该线程对该线程持有的AQS状态值减1,如果减去1后当前状态值为0,则当前线程会释放该锁,否则仅仅减1而已。

如果当前线程没有持有该锁而调用了该方法则会抛出IllegalMonitorStateException异常,代码如下。

在这里插入图片描述如代码(11)所示,如果当前线程不是该锁持有者则直接抛出异常,否则查看状态值是否为0,为0则说明当前线程要放弃对该锁的持有权,则执行代码(12)把当前锁持有者设置为null。如果状态值不为0,则仅仅让当前线程对该锁的可重入次数减1。

加深理解

在这里插入图片描述
假如线程Thread1、Thread2和Thread3同时尝试获取独占锁ReentrantLock,假设Thread1获取到了,则Thread2和Thread3就会被转换为Node节点并被放入ReentrantLock对应的AQS阻塞队列,而后被阻塞挂起。

假设Thread1获取锁后调用了对应的锁创建的条件变量1,那么Thread1就会释放获取到的锁,然后当前线程就会被转换为Node节点插入条件变量1的条件队列。

由于Thread1释放了锁,所以阻塞到AQS队列里面的Thread2和Thread3就有机会获取到该锁,假如使用的是公平策略,那么这时候Thread2会获取到该锁,从而从AQS队列里面移除Thread2对应的Node节点。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何使用静态IP代理解决Facebook多账号注册并进行网络推广业务?

在当今的数字时代&#xff0c;社交媒体成为了企业进行网络推广的一个重要途径&#xff0c;其中&#xff0c;Facebook是最受欢迎的社交媒体之一&#xff0c;因为它可以让企业通过创建广告和页面来推广他们的产品或服务。 但是&#xff0c;使用Facebook进行网络推广时&#xff0…

智能监控:业务监控新选择,效率提升新动力

前言 随着科技的飞速发展&#xff0c;企业对于业务的稳定性和连续性要求越来越高。传统的监控方式虽然在一定程度上能够保证业务的正常运行&#xff0c;但在面对复杂多变的业务场景和日益增长的数据量时&#xff0c;往往显得力不从心。为了解决这一问题&#xff0c;观测云在提…

金蝶云星空与阿里企业邮箱的对接轻松实现

通过数环通&#xff0c;您可以使用不到几分钟的时间即可实现金蝶云星空与阿里企业邮箱的对接与集成&#xff0c;从而高效实现工作流程自动化&#xff0c;降本增效&#xff01; 1.产品介绍 阿里企业邮箱是由阿里巴巴集团旗下的阿里云提供的一种专业的企业邮箱服务&#xff0c;具…

浅谈安科瑞直流表在孟加拉某能源公司的应用

摘要&#xff1a;本文介绍了安科瑞直流电表在孟加拉某能源公司的应用。主要用于光伏直流柜内&#xff0c;配合分流器对汇流箱的输出电流电压等进行测量&#xff0c;并采集配电现场的开关信号&#xff0c;装置带有RS485接口可以把测量和采集的数据和设备状态上传。 Abstract: T…

http介绍

http http工作原理 工作于客户端—服务端架&#xff08;client/server&#xff09;构上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。 Web服务器有&#xff1a;Nginx&#xff0c;Apache服务器&#xff0c;IIS服务器&#xff08;Internet Information …

大数据分析案例-基于LinearRegression回归算法构建房屋价格预测模型

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

Centos7 MongoDB安装

基础配置&#xff08;Centos7.5镜像&#xff0c;2核2GB内存&#xff0c;50GB硬盘&#xff09; 主机名IPmongodb192.168.100.124 &#xff08;在MongoDB官网有linux安装MongoDB的步骤&#xff09; &#xff08;1&#xff09;配置yum源 [rootmongodb ~]# cat /etc/yum.repos.d…

24/1/8

传奇登录界面&#xff1b; #include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {//窗口设置this->setWindowTitle("传奇");//窗口名this->setWindowIcon(QIcon("C:\\Users\\21528\\Desktop\\图标.png"));//窗口图标th…

用MATLAB求最短路径(graphshortestpath)和求最小生成树(minspantree),代码演示

求最短路径&#xff08;graphshortestpath&#xff09;&#xff0c;求最小生成树&#xff08;minspantree&#xff09; 文章目录 求最短路径&#xff08;graphshortestpath&#xff09;&#xff0c;求最小生成树&#xff08;minspantree&#xff09;1、最短路径问题2、最小生成…

Linux文件系统与日志管理

目录 一、Linux文件系统 1、inode 与 block 详解 1.1 inode 和 block 概述 1.2 inode表的内容 1.3 查看文件的inode号码 1.4 模拟innode号耗尽故障处理 2、访问文件的流程 3、文件恢复 3.1 恢复误删除的ext3格式文件 3.2 恢复误删除的 xfs 格式文件 二、Linux日志…

Java集合框架深度解析:HashMap

Java中的HashMap是一种基于哈希表的实现&#xff0c;提供了快速的查找性能。在这篇深度解析中&#xff0c;我们将深入探讨HashMap**的实现原理、适用场景、潜在问题以及并发控制策略。 1. HashMap的实现原理 1.1 哈希表 HashMap内部基于哈希表实现&#xff0c;通过散列函数将…

如何下载YouTube上的Mobile ALOHA视频?不用安装任何软件,一分钟搞定,一些好用的小技能又增加了^_^

一、背景 最近几天被斯坦福大学的移动双臂机器人——Mobile ALOHA刷屏了&#xff0c;就是下面这款能做饭&#xff0c;能收拾家务的机器人。因为是斯坦福大学研发的&#xff0c;他们的许多demo都上传到了国外的视频网站YouTube上面&#xff0c;为了防止未来的某天梯子不好用&am…

数据结构-测试5

一、判断题 1.二叉树只能用二叉链表表示&#xff08;F&#xff09; 二叉树的存储结构有两种&#xff0c;顺序存储结构和链式存储结构 2. 装填因子是散列表的一个重要参数&#xff0c;它反映散列表的装满程度。(T) 装填因子越小&#xff0c;发生冲突的可能性越小 3. 在任何情况…

CSS 发光输入框动画

<template><view class="content"><input placeholder="请输入..." class="input" /> </view> </template><script></script><style>/* 设置整个页面的背景颜色为 #212121 */body{background-c…

数据结构与算法(九)图链式存储

邻接表 度&#xff1a;无向图的度&#xff1a;顶点与邻接点连接的边就做度。有向图的度&#xff1a;指向顶点的边叫做入度&#xff0c;由顶点指向其他邻接点的边叫做出度 顶点&#xff1a;存储自身顶点信息和指向下一个临界点的指针 邻接点&#xff1a;保存临接点的存储下标…

基于海洋捕食者算法优化的Elman神经网络数据预测 - 附代码

基于海洋捕食者算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于海洋捕食者算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于海洋捕食者优化的Elman网络5.测试结果6.参考文献7.Matlab代码…

mysql索引失效的情况

目录 1破坏最左前缀法则2在索引列上做任何计算、函数操作&#xff0c;会导致索引失效而转向全表扫描。3存储引擎不能使用索引中范围条件右边的列4Mysql在使用不等于时无法使用索引会导致全表查询5is null可以使用索引&#xff0c;但是is not null无法使用索引6like以通配符开头…

网络调试 UDP1,开发板用动态地址-入门6

https://www.bilibili.com/video/BV1zx411d7eC?p11&vd_source109fb20ee1f39e5212cd7a443a0286c5 1, 开发板连接路由器 1.1&#xff0c;烧录无OS UDP例程 1.2&#xff0c;Mini USB连接电脑 1.3&#xff0c;开发板LAN接口连接路由器 2. Ping开发板与电脑之间通信* 2.1 根据…

消息队列-什么是MQ?何时使用MQ?怎么选择MQ?

什么是MQ&#xff1f; MessageQueue:就是消息 队列&#xff0c;任务队列&#xff0c;指令 队列。 功能&#xff1a;应用程序之间&#xff08;生产者与消费者&#xff09;的通信方式。 使用场景 从下面这个场景来感受MQ 的诞生 如果我们有很多任务需要处理&#xff0c;任务…

小白新手轻松部署扫雷小游戏

小白新手轻松部署扫雷小游戏 云效云效操作导入资源镜像仓库应用配置 最后 说到扫雷小游戏&#xff0c;可以说大家都玩儿过&#xff0c;印象中刚接触计算机的时候&#xff0c;对于这个扫雷小游戏&#xff0c;很多人都很喜欢&#xff0c;觉得很有意思&#xff0c;大家一起挑战看谁…