JUC锁: ReentrantReadWriteLock详解

简介: `ReentrantReadWriteLock` 主要用于实现高性能的并发读取,而在写操作相对较少的场景中表现尤为突出。它保证了数据的一致性和线程安全,在合适的场合合理使用 `ReentrantReadWriteLock`,可以实现更加细粒度的控制,并显著提升应用性能。然而,需要注意它的复杂度较一般的互斥锁高,因此在选择使用时要仔细考虑其适用场景。

ReentrantReadWriteLock 是 Java 并发包(java.util.concurrent.locks)中提供的一种读写锁实现,它提供了一种高效的读写分离锁机制,允许多个线程同时读取共享资源,但只允许一个线程进行写操作,从而在保证数据一致性的前提下,提高了并发访问的效率。

ReentrantReadWriteLock 的特点

  1. 重入性:该锁支持重入。读线程可以重复获取读锁,写线程也可以重复获取写锁。同时,写线程获取写锁后也能获取读锁,但是读线程拥有读锁后不能升级为写锁。
  2. 公平性选择:构造函数接受一个布尔值,来决定锁是公平的还是非公平的。公平锁意味着等待时间最长的线程会优先得到锁。
  3. 锁降级:写线程持有写锁状态下可以获取读锁,进而释放写锁,这种机制叫做锁降级,可以缩小数据不一致的窗口期。
  4. 读写分离:多个线程可以同时持有读锁,而写锁是独占的。这种读写分离的设计能在很多场景下提高性能,尤其是读多写少的场景。

如何使用

ReentrantReadWriteLock 包含两个主要部分:ReadLockWriteLock。在操作数据前,需要根据数据的读写操作获取对应的锁。

ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
Lock readLock = readWriteLock.readLock();
Lock writeLock = readWriteLock.writeLock();

// 获取读锁进行读取操作
readLock.lock();
try {
    // 执行读取操作
} finally {
    readLock.unlock();
}

// 获取写锁进行写入操作
writeLock.lock();
try {
    // 执行写入操作
} finally {
    writeLock.unlock();
}

源码分析

ReentrantReadWriteLock 内部通过使用两个类:ReadLockWriteLock 来分别管理读锁状态和写锁状态。锁状态是通过维护一个整数(一个 32 位的 int)来实现的,其中高 16 位代表读状态,低 16 位代表写状态。

写锁 的获取和释放都要先检查读锁状态,保证没有线程在执行读操作时才能获取写锁。写锁是独占的,一旦写锁被获取,其他线程无法获取读锁或写锁。

读锁 的获取则允许在没有写锁或者当前线程持有写锁时成功。这意味着读锁是可以与其他读锁共享的,但一旦有线程请求写锁,新来的读锁必须等待,以避免写锁饿死。

关于线程的等待队列,ReentrantReadWriteLock 按公平和非公平方式分别维护了两个队列。公平锁会在队列中严格按照等待时间来分配锁,而非公平锁则可能会允许在队列外的线程抢到锁,这会降低上下文切换的开销从而提升效率,但同时也带来了潜在的公平性问题。

适用场景

ReentrantReadWriteLock 是为读多写少的并发场景而设计的。在读操作远远多于写操作的情况下,使用读写锁可以比使用独占锁(如 ReentrantLock)显著提高并发性能。

写操作期间不允许任何读操作的发生,若写操作很少但耗时,最好是快速完成以避免长时间阻塞读操作。

结论

ReentrantReadWriteLock 主要用于实现高性能的并发读取,而在写操作相对较少的场景中表现尤为突出。它保证了数据的一致性和线程安全,在合适的场合合理使用 ReentrantReadWriteLock,可以实现更加细粒度的控制,并显著提升应用性能。然而,需要注意它的复杂度较一般的互斥锁高,因此在选择使用时要仔细考虑其适用场景。

目录
相关文章
|
Linux Docker 容器
Linux本地搭建StackEdit Markdown编辑器结合内网穿透实现远程访问
Linux本地搭建StackEdit Markdown编辑器结合内网穿透实现远程访问
|
设计模式 监控 安全
JUC第一讲:Java并发知识体系详解 + 面试题汇总(P6熟练 P7精通)
JUC第一讲:Java并发知识体系详解 + 面试题汇总(P6熟练 P7精通)
3868 0
|
SQL 关系型数据库 PostgreSQL
PostgreSQL datediff 日期间隔(单位转换)兼容SQL用法
标签 PostgreSQL , datediff 背景 使用datediff,对时间或日期相减,得到的间隔,转换为目标单位(日、月、季度、年、小时、秒。。。等)的数值。 DATEDIFF ( datepart, {date|timestamp}, {date|timestamp} ) 周...
16620 0
|
前端开发 JavaScript Java
如何使用 Spring Boot 和 Angular 开发全栈应用程序:全面指南
如何使用 Spring Boot 和 Angular 开发全栈应用程序:全面指南
504 1
|
9月前
|
存储 缓存 安全
【原理】【Java并发】【volatile】适合初学者体质的volatile原理
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是写出高端的CRUD应用。2025年,我正在沉淀自己,博客更新速度也在加快。在这里,我会分享关于Java并发编程的深入理解,尤其是volatile关键字的底层原理。 本文将带你深入了解Java内存模型(JMM),解释volatile如何通过内存屏障和缓存一致性协议确保可见性和有序性,同时探讨其局限性及优化方案。欢迎订阅专栏《在2B工作中寻求并发是否搞错了什么》,一起探索并发编程的奥秘! 关注我,点赞、收藏、评论,跟上更新节奏,让我们共同进步!
397 8
【原理】【Java并发】【volatile】适合初学者体质的volatile原理
|
SQL 前端开发 JavaScript
IDM 平替 Gopeed Flutter 开源免费下载工具
IDM 替代品 Gopeed 是一个开源免费的 Flutter 下载工具,支持 HTTP、BitTorrent、Magnet 等协议。项目采用 getx 进行构建,已获得 13k Star。功能包括多平台下载、自定义下载目录和并发数、代理设置等。它还拥有浏览器扩展和各种下载插件。开发者可以参考代码学习 Flutter 和 getx。项目源码可在 GitHub 上找到,同时提供了编译和配置指南。Gopeed 是一个值得尝试的现代化下载工具,适用于 Flutter 开发者和用户。
513 4
IDM 平替 Gopeed Flutter 开源免费下载工具
|
Java 应用服务中间件 Apache
浅谈Tomcat和其他WEB容器的区别
Tomcat是一款轻量级的免费开源Web应用服务器,常用于中小型系统及并发访问量适中的场景,尤其适合开发和调试JSP程序。它不仅能处理HTML页面,还充当Servlet和JSP容器。相比之下,物理服务器是指具备处理器、硬盘等硬件设施的服务器,如云服务器,其设计目标是在处理能力、稳定性和安全性等方面提供高标准服务。简言之,Tomcat专注于运行Java应用,而物理服务器则提供基础计算资源。
|
Java API
LOG4J2-MDC-全链路跟踪等功能研究
LOG4J2-MDC-全链路跟踪等功能研究
648 0
|
XML 缓存 JavaScript
优化Java中的XML解析性能
优化Java中的XML解析性能
|
自然语言处理 算法 Java
HanLP — 汉字转拼音,简繁转换 -- JAVA
HanLP — 汉字转拼音,简繁转换 -- JAVA
321 0