Java进阶篇之ReadWriteLock:让读取并行,让更新保持完整
上一篇通过Condition讨论了“业务条件什么时候满足”。接下来换一个常见场景一份共享数据被频繁查询偶尔更新。所有查询都排成一队有时会浪费并行机会放任读写交错又容易看到更新到一半的数据。ReadWriteLock为这类问题提供了读锁与写锁的配对协议。本文基于Java 21以ReentrantReadWriteLock为例先把访问边界划清再讨论它适合解决什么问题。一、读与写先看操作是否改变共享状态“查询方法”这个名字并不保证方法只读。例如获取数据时顺手更新访问次数、填充缓存就已经包含写操作。划分读写应检查实际行为。不同线程的访问组合是否允许同时持锁读与读可以读与写不可以写与写不可以同一个ReadWriteLock对象管理这一对锁。读路径和写路径若各自创建一个锁对象就失去了共同约束。遵守同一锁协议时写锁释放前的更新对后续获得对应读锁的线程可见。接口契约见Java 21 ReadWriteLock文档。二、把锁边界放在数据结构外层下面用商品目录作教学例子。内部是普通HashMap更新持有写锁读取时在读锁保护下复制快照然后把不可修改的结果交给调用者。图中的并排阅读对应多个读锁持有者等待更新的卡片对应写请求。它是访问规则的示意不表示公平队列中的精确调度顺序。三、完整示例读取快照再更新目录保存为ReadWriteDemo.java使用JDK21运行importjava.util.HashMap;importjava.util.Map;importjava.util.concurrent.*;importjava.util.concurrent.locks.ReentrantReadWriteLock;publicclassReadWriteDemo{staticclassCatalog{privatefinalMapString,IntegerpricesnewHashMap();privatefinalReentrantReadWriteLocklocknewReentrantReadWriteLock();voidput(Stringname,intprice){lock.writeLock().lock();try{prices.put(name,price);}finally{lock.writeLock().unlock();}}MapString,Integersnapshot(){lock.readLock().lock();try{returnMap.copyOf(prices);}finally{lock.readLock().unlock();}}}publicstaticvoidmain(String[]args)throwsException{CatalogcatalognewCatalog();catalog.put(book,42);try(ExecutorServicepoolExecutors.newFixedThreadPool(2)){FutureMapString,Integerfirstpool.submit(catalog::snapshot);FutureMapString,Integersecondpool.submit(catalog::snapshot);System.out.println(reader-1: first.get());System.out.println(reader-2: second.get());pool.submit(()-catalog.put(book,45)).get();System.out.println(after update: catalog.snapshot());}}}javac ReadWriteDemo.javajavaReadWriteDemo输出reader-1: {book42} reader-2: {book42} after update: {book45}这里通过Future.get明确等待两个查询结束再提交更新因此输出便于复现。线程池提供了两个工作线程但这段输出不能证明两次读取在时间上重叠也不能当作性能测试。示例重点是共享数据的访问封装。Map.copyOf在持锁期间复制结构返回后调用者无法修改这个Map。示例的String和Integer也是不可变对象。如果value换成可变对象浅复制仍会共享value引用应进一步复制数据或约束对象的修改方式。四、容易踩坑的地方升级、降级与复查ReentrantReadWriteLock支持写锁降级为读锁持有写锁时先取得读锁再释放写锁。这样从更新转入读取时中间没有允许其他写线程插入的空档。相反线程持有读锁时直接调用writeLock().lock不能完成读锁升级。需要更新时应先释放读锁再竞争写锁拿到写锁后重新检查业务状态。释放与重新获取之间别的线程可能已经完成了更新。具体约束见ReentrantReadWriteLock文档。例如“缓存无效则刷新”的流程应包含两次判断读锁内发现无效释放读锁并取得写锁后再判断是否仍然无效。第二次判断能避免多个线程接连做同一份刷新工作。降级适合“更新后需要继续在保护下使用数据”的情况。如果拿到的是完整不可变快照通常可以释放锁后处理快照结构也更简单。五、使用前再核对四个问题所有访问是否遵守协议不能一部分方法加锁另一部分直接操作内部Map也不要把内部可变集合原样返回。锁内是否有慢操作网络调用、文件访问和耗时计算可能长时间挡住写线程。可以先在锁外准备候选数据再用短写锁提交但提交前要检查版本或前提是否仍成立。读多是否就一定更快读写锁自身也有协调成本。数据很小、读操作极短或写入频繁时收益需要通过贴近实际负载的基准测试确认。本文不提供未经测量的加速比例。是否需要公平性或Condition默认构造是非公平策略可使用new ReentrantReadWriteLock(true)选择公平策略无参数tryLock不会遵守公平排队设置。只有写锁支持newCondition读锁调用会抛出UnsupportedOperationException。这也衔接了上一篇Condition的使用边界。对于独立key更新ConcurrentHashMap可能更合适需要协调多个字段、多个key或整体快照时可以考虑在共同的数据边界上使用读写锁。选择之前先写清楚一次业务操作必须一起保持一致的内容。六、 思维导图ReadWriteLock访问规则读读共享读写互斥数据边界同一对锁快照不泄漏可变状态锁转换写锁可降级释放读锁后重新检查使用取舍缩短持锁时间按实际负载测量七、总结总结要点读写分离的锁协议允许读取共享、更新独占。实际访问必须使用同一对锁内部数据不能从其他路径绕开保护。快照与对象边界同样重要。保护Map结构不等于保护其中所有可变对象返回值也应符合并发约定。锁转换与性能取舍需要具体分析。释放读锁后要复查状态降级按顺序完成是否值得引入读写锁应由实际工作负载验证。下一篇继续讨论StampedLock理解乐观读取为什么需要校验以及它与可重入读写锁的区别。如果你觉得这篇文章对你有所帮助欢迎点赞、收藏、分享

相关新闻

UVa 158日历题详解:闰年规则、星期计算与C++输出格式避坑

UVa 158日历题详解:闰年规则、星期计算与C++输出格式避坑

1. 先把题意拆明白:UVa 158 到底在考什么 1.1 输入输出长什么样 UVa 158 Calendar 这道题,几乎每个刷算法题的人都和它打过照面:题单里它排得很靠前,看着也简单——给一个年份和一个月份,把整个月的日历按星期排出来。…

2026/9/30 8:55:24 阅读更多 →
SpringBoot面包加盟店管理系统:从业务拆解到部署调试全解析

SpringBoot面包加盟店管理系统:从业务拆解到部署调试全解析

Springboot面包加盟店管理系统,听起来像是个典型的毕业设计课题,但你要是真把它当“又一个CRUD项目”来对待,那就亏了。做这类加盟连锁管理系统,核心难点不在于某个技术多前沿,而在于业务模型怎么拆、角色权限怎么分、…

2026/10/1 9:00:55 阅读更多 →
YOLOv11铁路安全检测:轨道异物识别与列车故障诊断落地指南

YOLOv11铁路安全检测:轨道异物识别与列车故障诊断落地指南

简介:基于YOLOv11的铁路安全检测专题文档,围绕轨道异物识别与列车部件故障诊断两大场景,系统讲解从YOLO系列算法演进至YOLOv11核心原理、网络结构、特征提取与代码解读。内容覆盖轨道异物识别系统和列车部件故障诊断系统的完整开发流程&#…

2026/9/30 8:55:24 阅读更多 →

最新新闻

因果模型与因果推断入门:从DAG、Uplift到去混淆建模

因果模型与因果推断入门:从DAG、Uplift到去混淆建模

1. 从"相关不等于因果"说起:为什么做模型的人迟早要补上这一课做推荐、风控、增长的同学大概都经历过这种场面:离线模型的AUC涨了两个点,上线之后核心指标纹丝不动,甚至倒退;反过来,某个拍脑袋的…

2026/10/1 16:56:56 阅读更多 →
北邮编译原理课程设计:词法分析到目标代码生成全流程拆解

北邮编译原理课程设计:词法分析到目标代码生成全流程拆解

简介:这份资源是北京邮电大学编译原理课程设计的完整实践项目,面向正在学习编译原理、需要动手实现编译器的计算机专业学生。项目以Pascal语言为源语言,覆盖词法分析、语法分析、语义分析、代码生成及符号表管理、错误处理、中间代码生成等核…

2026/10/1 16:56:56 阅读更多 →
HowToCook 闽南风味炒年糕:葱油提香的软糯主食完整实战指南

HowToCook 闽南风味炒年糕:葱油提香的软糯主食完整实战指南

文档教程 【免费下载链接】HowToCook Programmers guide about how to cook at home. 项目地址: https://gitcode.com/GitHub_Trending/ho/HowToCook 点击查看 免费下载 导读 本篇技术指南基于 HowToCook 开源项目中的 炒年糕.md 菜谱文档,系统讲解以米…

2026/10/1 16:56:56 阅读更多 →
Anaconda 安装、conda 环境与 Navigator 闪退排查

Anaconda 安装、conda 环境与 Navigator 闪退排查

1. Anaconda 到底解决了什么问题,Navigator 又在其中扮演什么角色刚入行的朋友问我最多的一句话是:"我不就是想写个 Python 脚本吗,为什么非要装 Anaconda?"这个疑问很合理,因为如果你只写单文件脚本、只用标…

2026/10/1 16:56:56 阅读更多 →
SystemVerilog中的automatic关键字:从存储类型到并发安全的完整解析

SystemVerilog中的automatic关键字:从存储类型到并发安全的完整解析

最近在复盘System Verilog面试题的时候,我总会被同一个关键字击中——automatic。说句实话,这可能是整个SV里“看起来最简单、用起来最嚣张”的一个关键字。很多朋友背了一堆概念,知道“函数加automatic可以递归”,可在真实仿真环…

2026/10/1 16:56:56 阅读更多 →
Linux目录结构深度解析:FHS、/bin、/etc与挂载点

Linux目录结构深度解析:FHS、/bin、/etc与挂载点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 16:55:56 阅读更多 →

日新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 1:01:17 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/30 13:14:22 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/30 18:13:06 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/9/30 13:14:49 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 1:01:17 阅读更多 →