当前位置: 首页 > news >正文

JDK1.8 ReentrantLock相关源码

java.util.concurrent.locks.ReentrantLock内部维护了AbstractQueuedSynchronizer的实现类java.util.concurrent.locks.ReentrantLock.Sync,主要是基于AbstractQueuedSynchronizer实现的一些同步功能,

其中AbstractQueuedSynchronizer内部也是基于LockSupport.park(this);LockSupport.unpark(node.thread)的基本加锁解锁功能和大量的CAS 操作,实现数据的同步和线程的调度功能,为其他线程相关操作类提供了一个基础的工具类。

其中CAS操作系统调用代价低,原因如下(参考AI回答)

1. 硬件级原子指令支持

  • CAS 操作通过 CPU 提供的‌单条原子指令‌(如 x86 的 cmpxchg、ARM 的 ldrex/strex)直接实现比较与交换操作,‌无需进入内核态‌。
  • 该指令在单个时钟周期完成内存值与预期值的比对和更新,‌完全在用户态执行‌,避免了系统调用上下文切换的开销。

2. 无锁化设计减少阻塞

  • 无锁自旋‌:CAS 通过循环重试(自旋)处理竞争,线程始终处于运行状态,‌无线程挂起/唤醒操作‌,规避了内核调度成本。
  • 对比锁机制‌:传统锁(如 synchronized)在竞争失败时触发线程阻塞,涉及内核态切换(如 futex 系统调用)和上下文保存/恢复,开销显著高于自旋。

3. 内核交互的极简触发

  • CAS ‌仅在发生竞争时依赖用户态指令‌,不强制与内核交互。
  • 仅当多核 CPU 缓存不一致时,通过 ‌MESI 协议或总线锁定‌协调缓存(硬件层面完成),‌极少触发内核介入

4. 性能对比数据

场景CAS 机制synchronized差异来源
无竞争操作≈10ns(用户态CAS指令)≈20ns(偏向锁CAS)67
单一竞争≈100ns(自旋数次)>1000ns(锁膨胀+内核阻塞)47
高并发竞争自旋消耗CPU(需控制重试)阻塞队列调度稳定412

 java.util.concurrent.locks.ReentrantLock#lock

NonfairSync和FairSync继承Sync,分别实现了一些非公平和公平获取锁的逻辑,其中FairSync基于NonfairSync。

源码中206行可知,AQS中状态为0代表为线程占用,1代表有线程占用。所谓的锁,本质是一些数据标识。setExclusiveOwnerThread方法代表当前线程获取锁成功,设置为AQS中的独占线程。如获取锁失败,再进入209行尝试获取锁。

 进入java.util.concurrent.locks.ReentrantLock.NonfairSync#tryAcquire====>java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire

 从131-137行可以看出新来的线程可以直接根据当前状态判断是否能拿到锁,不需要和AQS队列里的线程竞争,这就是非公平的体现。138行-144行看出如果当前线程已经是AQS中的独占线程是,再加锁时,状态会叠加,体现了可重入的特性(这段代码中当前线程是独占执行的,不需要CAS操作)。

 如果nonfairTryAcquire获取失败,会先进入

java.util.concurrent.locks.AbstractQueuedSynchronizer#addWaiter

该方法主要是创建AQS双向链表中的节点,并加入链表的尾部,每个节点封装了线程对象。

再进入java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireQueued

这个方法主要是循环获取锁,如获取不到则阻塞当前线程,此时当前线程已在AQS链表中,可有其他线程(链表中前一个节点)唤醒。从862-868行看出获取锁是从链表的头部节点开始的。

进入java.util.concurrent.locks.AbstractQueuedSynchronizer#shouldParkAfterFailedAcquire。可以看出,前一节点waitStatus状态必须为Node.SIGNAL,当前节点才能阻塞。因为阻塞节点的唤醒必须基于前一节点waitStatus状态。808-811行类似以lazy方式根据状态更新双向链表

java.util.concurrent.locks.ReentrantLock#unlock

java.util.concurrent.locks.ReentrantLock#unlock底层调用了java.util.concurrent.locks.AbstractQueuedSynchronizer#release方法

 在java.util.concurrent.locks.ReentrantLock.Sync#tryRelease方法中,当前AQS的state字段减去参数arg,当state为0时,释放锁,唤醒其他线程。

http://www.dtcms.com/a/278302.html

相关文章:

  • 代数基本定理
  • 多模态数据处理新趋势:阿里云ODPS技术栈深度解析与未来展望
  • RabbitMQ中队列长度限制(Queue Length Limit)详解
  • LVS的集群技术和分布式
  • hive的相关的优化
  • 传统机器学习在信用卡交易预测中的卓越表现:从R²=-0.0075到1.0000的华丽转身
  • Android 性能优化:启动优化全解析
  • Android 16系统源码_窗口动画(一)窗口过渡动画层级图分析
  • USB读写自动化压力测试
  • Android编译系统——基础介绍(一)
  • 微软发布BioEmu模型
  • spring shell 基础使用
  • PyTorch生成式人工智能(17)——变分自编码器详解与实现
  • 大话数据结构之 <链表>(C语言)
  • 8.服务通信:Feign深度优化 - 解密声明式调用与现代负载均衡内核
  • UV vs Pip:Python 包管理的革命性进化
  • 017 进程控制 —— 终止进程
  • CentOS 7服务器上使用Docker部署Notesnook的详细指导说明
  • Python爬虫打怪升级:数据获取疑难全解析
  • 天地图前端实现geoJson与wkt格式互转
  • C++面试5题--4day
  • Java陷阱之assert关键字详解
  • 使用layui的前端框架过程中,无法加载css和js怎么办?
  • 谷歌开源库gtest 框架安装与使用
  • CentOS系统哪些版本?分别适用于那些业务或网站类型?
  • 2025年Java后端社招面试:高频场景题+八股文面试题解析
  • toString
  • S7-1200 中 AT 覆盖参数的应用:灵活访问数据区域的实用指南
  • 借助DeepSeek编写输出漂亮表格的chdb客户端
  • 电流驱动和电压驱动的区别