深入浅出Java并发读写锁ReentrantReadWriteLock(读锁)
读锁详解读锁的获取看完了写锁再来看看读锁读锁不是独占式锁即同一时刻该锁可以被多个读线程获取也就是一种共享式锁。按照之前对 AQS 的介绍实现共享式同步组件的同步语义需要通过重写 AQS 的 tryAcquireShared 方法和 tryReleaseShared 方法。读锁的获取实现方法为protected final int tryAcquireShared(int unused) { /* * Walkthrough: * 1. If write lock held by another thread, fail. * 2. Otherwise, this thread is eligible for * lock wrt state, so ask if it should block * because of queue policy. If not, try * to grant by CASing state and updating count. * Note that step does not check for reentrant * acquires, which is postponed to full version * to avoid having to check hold count in * the more typical non-reentrant case. * 3. If step 2 fails either because thread * apparently not eligible or CAS fails or count * saturated, chain to version with full retry loop. */ Thread current Thread.currentThread(); int c getState(); //1. 如果写锁已经被获取并且获取写锁的线程不是当前线程的话当前 // 线程获取读锁失败返回-1 if (exclusiveCount(c) ! 0 getExclusiveOwnerThread() ! current) return -1; int r sharedCount(c); if (!readerShouldBlock() r MAX_COUNT //2. 当前线程获取读锁 compareAndSetState(c, c SHARED_UNIT)) { //3. 下面的代码主要是新增的一些功能比如getReadHoldCount()方法 //返回当前获取读锁的次数 if (r 0) { firstReader current; firstReaderHoldCount 1; } else if (firstReader current) { firstReaderHoldCount; } else { HoldCounter rh cachedHoldCounter; if (rh null || rh.tid ! getThreadId(current)) cachedHoldCounter rh readHolds.get(); else if (rh.count 0) readHolds.set(rh); rh.count; } return 1; } //4. 处理在第二步中CAS操作失败的自旋已经实现重入性 return fullTryAcquireShared(current); }代码的逻辑请看注释需要注意的是当写锁被其他线程获取后读锁获取失败否则获取成功会利用 CAS 更新同步状态。另外当前同步状态需要加上 SHARED_UNIT(1 SHARED_SHIFT)即 0x00010000的原因我们在上面也说过了同步状态的高 16 位用来表示读锁被获取的次数。如果 CAS 失败或者已经获取读锁的线程再次获取读锁时是靠 fullTryAcquireShared 方法实现的。读锁的释放读锁释放的实现主要通过方法 tryReleaseShared源码如下主要逻辑请看注释protected final boolean tryReleaseShared(int unused) { Thread current Thread.currentThread(); // 前面还是为了实现getReadHoldCount等新功能 if (firstReader current) { // assert firstReaderHoldCount 0; if (firstReaderHoldCount 1) firstReader null; else firstReaderHoldCount--; } else { HoldCounter rh cachedHoldCounter; if (rh null || rh.tid ! getThreadId(current)) rh readHolds.get(); int count rh.count; if (count 1) { readHolds.remove(); if (count 0) throw unmatchedUnlockException(); } --rh.count; } for (;;) { int c getState(); // 读锁释放 将同步状态减去读状态即可 int nextc c - SHARED_UNIT; if (compareAndSetState(c, nextc)) // Releasing the read lock has no effect on readers, // but it may allow waiting writers to proceed if // both read and write locks are now free. return nextc 0; } }锁降级读写锁支持锁降级遵循按照获取写锁获取读锁再释放写锁的次序写锁能够降级成为读锁不支持锁升级关于锁降级下面的示例代码摘自 ReentrantWriteReadLock 源码void processCachedData() { rwl.readLock().lock(); if (!cacheValid) { // Must release read lock before acquiring write lock rwl.readLock().unlock(); rwl.writeLock().lock(); try { // Recheck state because another thread might have // acquired write lock and changed state before we did. if (!cacheValid) { data ... cacheValid true; } // Downgrade by acquiring read lock before releasing write lock rwl.readLock().lock(); } finally { rwl.writeLock().unlock(); // Unlock write, still hold read } } try { use(data); } finally { rwl.readLock().unlock(); } }这里的流程可以解释如下获取读锁首先尝试获取读锁来检查某个缓存是否有效。检查缓存如果缓存无效则需要释放读锁因为在获取写锁之前必须释放读锁。获取写锁获取写锁以便更新缓存。此时可能还需要重新检查缓存状态因为在释放读锁和获取写锁之间可能有其他线程修改了状态。更新缓存如果确认缓存无效更新缓存并将其标记为有效。写锁降级为读锁在释放写锁之前获取读锁从而实现写锁到读锁的降级。这样在释放写锁后其他线程可以并发读取但不能写入。使用数据现在可以安全地使用缓存数据了。释放读锁完成操作后释放读锁。这个流程结合了读锁和写锁的优点确保了数据的一致性和可用性同时允许在可能的情况下进行并发读取。使用读写锁的代码可能看起来比使用简单的互斥锁更复杂但它提供了更精细的并发控制可能会提高多线程应用程序的性能。使用读写锁ReentrantReadWriteLock 的使用非常简单下面的代码展示了如何使用 ReentrantReadWriteLock 来实现一个线程安全的计数器public class Counter { private final ReentrantReadWriteLock rwl new ReentrantReadWriteLock(); private final Lock r rwl.readLock(); private final Lock w rwl.writeLock(); private int count 0; public int getCount() { r.lock(); try { return count; } finally { r.unlock(); } } public void inc() { w.lock(); try { count; } finally { w.unlock(); } } }当缓存无效时会先释放读锁然后获取写锁来更新缓存。一旦缓存被更新就会进行写锁到读锁的降级允许其他线程并发读取但仍然排除写入。这样的结构允许在确保数据一致性的同时实现并发读取的优势从而提高多线程环境下的性能。

相关新闻

【2026年版|必收藏】Agent全面爆发!万字长文详解上下文工程(小白/程序员入门必备)

【2026年版|必收藏】Agent全面爆发!万字长文详解上下文工程(小白/程序员入门必备)

1、Agent 全面爆发的前夜:上下文正在成为核心变量(2026年实践更新) 1.1、从 Chatbot 到 Agent:2026年,能力形态的本质跃迁** 在大语言模型(LLM)规模化落地之前,Chatbot 是最主流的…

2026/7/5 18:16:31 阅读更多 →
携手NVIDIA,英飞凌为人形机器人提供精准运动与高效解决方案

携手NVIDIA,英飞凌为人形机器人提供精准运动与高效解决方案

电机驱动芯片 全球功率系统和物联网领域的半导体领导者英飞凌科技股份公司(FSE代码:IFX/OTCQX代码:IFNNY)宣布携手NVIDIATechnology(简称:NVIDIA英伟达,下同)加速人形机器人领域的研…

2026/7/4 8:32:52 阅读更多 →
基于大数据技术的产品评价分析系统[python]-计算机毕业设计源码+LW文档

基于大数据技术的产品评价分析系统[python]-计算机毕业设计源码+LW文档

摘要:随着电子商务的迅猛发展,产品评价数据呈爆炸式增长。本文阐述了一个基于大数据技术的产品评价分析系统的设计与实现。该系统借助大数据采集、存储、处理和分析技术,对海量产品评价数据进行深度挖掘。通过自然语言处理技术理解评价内容语…

2026/7/5 2:36:35 阅读更多 →

最新新闻

2026最新2款AI编程工具平替之选深度实测

2026最新2款AI编程工具平替之选深度实测

上周花了整周时间,我把 5 款 AI 编程工具分别用在 5 个不同模块上——一个工具一个模块,看最终代码质量差异。我当时选的模块里就包含了Node.js Express的用户行程文件上传功能,测试过程里我全程用vibe coding的方式,只靠口述需求…

2026/7/6 1:31:36 阅读更多 →
Halcon 标定板像素当量标定:单图法 vs 多图法,3种场景精度对比实测

Halcon 标定板像素当量标定:单图法 vs 多图法,3种场景精度对比实测

Halcon 标定板像素当量标定:单图法 vs 多图法,3种场景精度对比实测在工业视觉测量领域,像素当量标定的精度直接影响着整个系统的测量准确性。面对产线节拍和精度的双重需求,工程师们常常需要在单图快速标定与多图高精度标定之间做…

2026/7/6 1:29:36 阅读更多 →
华为matepad pro运行jupyter

华为matepad pro运行jupyter

想着在平板上跑跑Python,也不做太大强度的,主要学学数据分析,找了一些技术帖,先尝试了aidlux,内置的aidcode界面不太喜欢,jupyterlab运行起来kernel一直提示disconnected,遂作罢,最后…

2026/7/6 1:29:36 阅读更多 →
WK2124 SPI扩展8串口实战:Linux驱动配置与双芯片中断共享方案

WK2124 SPI扩展8串口实战:Linux驱动配置与双芯片中断共享方案

WK2124 SPI扩展8串口实战:Linux驱动配置与双芯片中断共享方案 在嵌入式系统开发中,串口资源不足是工程师经常面临的挑战。主控芯片通常只提供有限的UART接口,而实际应用却需要连接多个外设——从GPS模块、RFID读卡器到工业传感器和调试终端。…

2026/7/6 1:27:36 阅读更多 →
动量守恒定律与动能定理联立求解:3步构建经典碰撞问题分析框架

动量守恒定律与动能定理联立求解:3步构建经典碰撞问题分析框架

动量守恒与动能定理联立求解:三步构建碰撞问题通用分析框架在经典力学问题中,碰撞分析一直是大学物理课程的核心难点之一。许多同学面对题目时往往陷入两种困境:要么机械套用公式导致解题方向错误,要么面对多定理选择时无所适从。…

2026/7/6 1:27:35 阅读更多 →
t检验、Mann-Whitney U等6组方法对比:正态/非正态数据下的检验效能与样本量模拟

t检验、Mann-Whitney U等6组方法对比:正态/非正态数据下的检验效能与样本量模拟

正态与非正态数据下的统计检验效能对比:6种方法的Python模拟与样本量公式推导当数据科学家面对两组数据比较的任务时,第一个浮现在脑海中的问题往往是:"该用t检验还是Mann-Whitney U检验?"这个看似简单的选择背后&#…

2026/7/6 1:25:35 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻