ReentrantLock源码浅析

一、ReentrantLock概念

ReentrantLock是JAVA并发情况下提供的用来加锁的机制,位于JUC包下,提供了一系列的加锁释放锁的方法,使用起来非常简单,只需要在代码块之前调用lock()方法,在finally中调用unlock()方法即可解决并发的问题。

1.1、AQS

AQS实际上是AbstractQueuedSynchronizer的缩写,是JAVA提供的用来实现锁的一个抽象类,它有一个基类AbstractOwnableSynchronizer,可以理解为它为实现ReentrantLock等其他锁提供了一个基础的容器,这些容器在不同的锁的实现中都存放着实现这些锁所需要的基本组件。

  • 1、Node:AQS中的内部类,代表着一个节点,内部有一个Thread线程,每一个线程竞争锁的时候,都会被封装成一个Node放在队列中排队。
  • 2、exclusiveOwnerThread:这是AQS的基类中的变量,用来表示当前已经竞争到锁的线程,
  • 3、state:AQS中的变量,是一个volatile int类型的变量,线程能不能竞争到锁,就看线程能不能通过CAS的方式将该变量从0改成1。
  • 4、head:AQS中的变量,是一个volatile Node类型的变量,用来标识头结点。
  • 5、tail:AQS中的变量,是一个volatile Node类型的变量,用来标识尾节点。
1.2、CAS

CAS相关知识,参考本人博客:JAVA CAS问题原理

二、ReentrantLock源码流程

2.1、lock():

实际上会调用sysn的lock(),而sync在ReentrantLock中有两种实现,也就是FairSync(公平锁)和NonfairSync(非公平锁),默认为NonfairSync。

public void lock() {  // 默认调用NonfairSync中的lock()sync.lock();  
}
2.2、NonfairSync中的lock()
final void lock() { // 因为是非公平锁,一上来就先竞争锁,if (compareAndSetState(0, 1))  // 如果获取到锁,就将exclusiveOwnerThread变量设置为自己,表明当前是自己获取到锁了。setExclusiveOwnerThread(Thread.currentThread());  else  // 如果没有获取到锁,那么就开始排队acquire(1);  
}
2.3、acquire()方法是AQS中提供的方法
public final void acquire(int arg) {  // 再次尝试获取锁,如果获取锁失败那么就将当前线程封装成Node节点开始排队if (!tryAcquire(arg) &&  acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 并且将当前线程设置为停止状态,也就是说,放弃竞争了。 selfInterrupt();  
}
2.4、addWaiter()方法是AQS中提供的方法
private Node addWaiter(Node mode) {  // 首先将当前线程封装成一个Node节点Node node = new Node(Thread.currentThread(), mode);  // 存储头结点Node pred = tail;  // 如果头结点不为null,if (pred != null) { // 当前节点的前一个节点指向tailnode.prev = pred;  // 并且通过CAS的方式将当前节点和tail进行交换if (compareAndSetTail(pred, node)) {  // 如果当前节点和tail进行交换成功,头结点的下一个节点指向当前节点,并返回当前节点pred.next = node;  return node;  }  } // 如果头节点为空,说明当前节点是第一个节点,队列中没有其他节点 enq(node);  return node;  
}
2.5、enq()方法是AQS中提供的方法
private Node enq(final Node node) {  for (;;) {  // 将头节点进行缓存Node t = tail;  // 如果头结点为null,说明此时队列中没有其他节点,只有当前节点一个if (t == null) {// 创建一个新的节点,该节点没有任何意义,只是AQS实现上为了帮助后续唤醒线程而设计if (compareAndSetHead(new Node()))  // 头尾指向同一个节点tail = head;  } else {  // 如果头结点不为null,当前节点的前一个节点指向tailnode.prev = t;  // 通过CAS方式设置当前节点为头节点if (compareAndSetTail(t, node)) {// 头结点的下一个节点指向当前节点 t.next = node;  return t;  }  }  }  
}
2.6、acquireQueued()方法是AQS中提供的方法
final boolean acquireQueued(final Node node, int arg) {  boolean failed = true;  try {  boolean interrupted = false;  for (;;) { // 实际上就是在获取当前节点的前一个节点 final Node p = node.predecessor();  // 如果当前节点是头结点,那就说明它前边没有其他节点竞争锁,那么自己就开始获取锁if (p == head && tryAcquire(arg)) {  // 如果加锁成功,设置当前节点为头结点setHead(node); // 将当前节点的前一个节点(实际上就是头结点)设置为null,p.next = null; // help GC  failed = false; // 只有在竞争不到锁的情况下,才是true,表明停止当前线程,放弃竞争锁return interrupted;  }  // 当线程竞争锁失败后,是不是需要暂停线程if (shouldParkAfterFailedAcquire(p, node) &&  parkAndCheckInterrupt())  interrupted = true;  }  } finally {  if (failed) // 当线程竞争锁失败后,需要取消竞争 cancelAcquire(node);  }  
}
2.7、nonfairTryAcquire()是Sync中提供的竞争锁的方法。
final boolean nonfairTryAcquire(int acquires) { // 获取当前线程final Thread current = Thread.currentThread();  // 获取state变量值int c = getState(); // 如果state变量值为0,说明当前没有线程竞争锁或者锁已经被释放,那么就代表,当前线程可以竞争锁 if (c == 0) { // 通过CAS操作将state将由0改成1,if (compareAndSetState(0, acquires)) {  // 如果成功,就获取到锁,将当前线程设置为自己并返回true,表明加锁成功。setExclusiveOwnerThread(current);  return true;  }  }  // 如果state的值不为0,那么就是判断当前线程是不是自身线程,如果是,说明本身已经获取到锁了,就不在需要重复竞争锁了,实际上就是可重入锁的判断else if (current == getExclusiveOwnerThread()) {  // 可重入次数的判断,实际上ReentrantLock中不可能出现这种情况,这里知识容错处理int nextc = c + acquires;  if (nextc < 0) // overflow  throw new Error("Maximum lock count exceeded");  // 将state的值设置为重入的次数,并返回true,表明加锁成功。setState(nextc);  return true;  }  // 否则其他情况都是加锁失败的情况return false;  
}

三、ReentrantLock总结

1、ReentrantLock可以实现公平锁和非公平锁以及可重入锁的机制,基于AQS和CAS进行加锁处理, 提供了lock和unlock两个api方法进行加锁和释放锁处理。
2、以非公平锁为例,lock方法一进去就竞争锁,通过CAS的方式将state变量从0设置为1,如果设置成功,表明竞争锁成功,就将线程变量设置为当前线程,如果失败,就开始排队。
3、再次尝试获取锁(tryAcquire),如果继续失败,那么就放弃竞争锁,将自己状态设置为interrupted的。并且开始排队。
4、排队的时候,将自身线程封装成一个Node节点,找到一个不失效的前继节点,当前节点的pred指向前继节点,前继节点的next指向当前节点。
4、可重入的判断,实际上,如果发现当前state不为0,那就说明有线程正在占用资源,那么只需要判断current == getExclusiveOwnerThread()是否成立即可,如果成立就是可重入的,获得锁,如果不成立,就说明,当前占用资源的线程不是自身,获取锁失败。

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

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

相关文章

Postman接收列表、数组参数@RequestParam List<String> ids

示例如下: 接口定义如下: GetMapping(value "/queryNewMoviePath")public List<Map<String, Object>> queryNewMoviePath(RequestParam List<String> ids ) {return service.queryNewMoviePath(ids);}postman中测试如下&#xff1a; http://loc…

【Spring篇】使用注解进行开发

&#x1f38a;专栏【Spring】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 &#x1f970;欢迎并且感谢大家指出小吉的问题 文章目录 &#x1f33a;原代码&#xff08;无注解&#xff09;&#x1f384;加上注解⭐两个注…

开发模型>螺旋模型

螺旋模型是在快速原型的基础上扩展而成的一种生存周期模型。这种模型将整个软件开发流程分成多个阶段&#xff0c;每个阶段都由4部分组成&#xff0c;它们是&#xff1a; ① 目标设定。为该项目进行需求分析&#xff0c;定义和确定这一个阶段的专门目标&#xff0c;指定对过程和…

分布式下多节点WebSocket消息收发

1、使用场景 2、疑问 第一次发送请求后&#xff0c;通过N1&#xff0c;W2&#xff0c;到达service2&#xff0c;建立websocket连接。 1、接下来发送的消息&#xff0c;通过Ngixn后和网关gateway后还能落在service2上面吗&#xff1f; 如果不能落在service2上&#xff0c;需要怎…

互联网医院系统:数字化时代中医疗服务的未来

随着数字化时代的发展&#xff0c;互联网医院系统在医疗服务中的作用日益凸显。本文将讨论互联网医院系统的一些关键技术方面&#xff0c;探讨这些技术如何推动医疗服务进入数字化时代。 1. 数据智能与个性化服务 互联网医院系统依赖于大数据分析和人工智能技术&#xff0c;…

python文件操作之xml转txt

在使用yolo进行深度学习训练时&#xff0c;我们所使用的标签文件都是txt格式的&#xff0c;但是有的人使用的标注软件生成的可能是xml文件&#xff0c;那么就需要使用python工具写一个格式转换脚本。 首先导入库&#xff0c;并定义标注的图片地址、生成的标签文件xml地址、存储…

Spring Boot使用EhCache完成一个缓存集群

在上一篇在SpringBoot中使用EhCache缓存&#xff0c;我们完成了在Spring Boot中完成了对EhCaChe的使用&#xff0c;这篇&#xff0c;我们将对EhCache的进一步了解&#xff0c;也就是搭建一个EhCache的缓存集群。 集群 在搭建一个EhCache的时候&#xff0c;我们需要先了解&…

2023-11-17 VsCode使用makefile进行多文件编译

点击 <C 语言编程核心突破> 快速C语言入门 VsCode使用makefile进行多文件编译 前言一、一个简单的多文件示例二、makefile基本语法三、VsCode使用makefile总结 前言 要解决问题: C或C可以多文件编译, 意味着需要进行代码组织, 为了方便多文件编译, gnu开发了make工具, …

2018年五一杯数学建模A题徐州潘安湖风景区游览路线设计解题全过程文档及程序

2019年五一杯数学建模 A题 徐州潘安湖风景区游览路线设计 原题再现 徐州是一个老工业基地和资源型城市&#xff0c;煤炭开采历史长达130年。长期煤炭开采在徐州累计形成采煤塌陷区达数十万亩。位于徐州市贾汪区西南部、紧邻马庄的潘安湖湿地公园原来就是徐州最大的、塌陷最严…

实验六:Android的网络编程基础

实验六&#xff1a;Android 的网络编程基础 6.1 实验目的 本次实验的目的是让大家熟悉 Android 开发中的如何获取天气预报&#xff0c;包括了 解和熟悉 WebView、WebService 使用、网络编程事件处理等内容。 6.2 实验要求 熟悉和掌握 WebView 使用 了解 Android 的网络编程…

Hadoop学习总结(MapRdeuce的词频统计)

MapRdeuce编程示例——词频统计 一、MapRdeuce的词频统计的过程 二、编程过程 1、Mapper 组件 WordcountMapper.java package com.itcast.mrdemo;import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; …

网站页头被挂马状态及新增了index.html文件解决思路

今天网站刚新增了篇了文章《从nginx层阻断可执行的php 防止宝塔站点挂马》,整体测试下来还是不靠谱,设置后导致所有PHP文件都打不开了。 经过不断的查看日志和搜索办法总算告一段落,后续待观察。原因如下,多个网站目录新增了index.html文件,看时间是近两天上传的。 网站代…

微信小程序动态生成表单来啦!你再也不需要手写表单了!

dc-vant-form 由于我们在小程序上涉及到数据采集业务&#xff0c;需要经常使用表单&#xff0c;微信小程序的表单使用起来非常麻烦&#xff0c;数据和表单是分离的&#xff0c;每个输入框都需要做数据处理才能实现响应式数据&#xff0c;所以我开发了dc-vant-form&#xff0c;…

【开源】基于Vue.js的社区买菜系统的设计和实现

项目编号&#xff1a; S 011 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S011&#xff0c;文末获取源码。} 项目编号&#xff1a;S011&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、系统设计2.1 功能模块设计2.1.1 数据中心模块2.1…

【uniapp】 video视频层级、遮挡其他弹窗或顶部导航 使用nvue覆盖

uniapp 顶部导航和弹窗被video遮挡解决办法 第一步&#xff1a;配置 subNVues {"path": "pages/index/index","style": {"navigationBarTitleText": "uni-app","navigationStyle": "custom","app-…

对话芯动科技 | 助力云游戏 4K级服务器显卡的探索与创新

2021年芯动科技推出了基于IMG BXT GPU IP的风华1号显卡。单块风华1号显卡可在台式机和云游戏中实现4K级别的性能&#xff0c;渲染能力达到5 TFLOPS&#xff0c;如果在服务器中同时运行两块显卡&#xff0c;性能还可翻倍。该显卡是为不断扩大的安卓云游戏市场量身定制的&#xf…

隐私协议 Secret Network 宣布使用 Octopus Network 构建的 NEAR-IBC 连接 NEAR 生态

2023年11月 NearCon2023 活动期间&#xff0c;基于 Cosmos SDK 构建的隐私协议 Secret Network&#xff0c;宣布使用 Octopus Network 开发的 NEAR-IBC&#xff0c;于2024年第一季度实现 Secret Network 与 NEAR Protocol 之间的跨链交互。 这将会是Cosmos 生态与 NEAR 之间的首…

GZ033 大数据应用开发赛题第04套

2023年全国职业院校技能大赛 赛题第04套 赛项名称&#xff1a; 大数据应用开发 英文名称&#xff1a; Big Data Application Development 赛项组别&#xff1a; 高等职业教育组 赛项编号&#xff1a; GZ033 …

[C国演义] 哈希的使用和开闭散列的模拟实现

哈希的使用和开闭散列的模拟实现 1. 使用1.1 unordered_map的接口1.2 unordered_set的接口 2. 哈希底层2.1 概念2.2 解决哈希冲突 3. 实现3.1 开放寻址法3.2 拉链法 1. 使用 1.1 unordered_map的接口 构造 void test1() {// 空的unordered_map对象unordered_map<int, in…