30.jdk源码阅读之ReentrantReadWriteLock

1.写在前面

在这里插入图片描述
ReentrantReadWriteLock 是 Java 并发包中的一个读写锁实现,它允许多个读线程同时访问共享资源,但在写线程访问时,所有的读线程和其他写线程都会被阻塞。不知道大家在日常工作中这个类使用的多不多,对于它的底层实现有没有思考过,比如下面几个问题大家可以看看日常有没有遇到过?

  1. ReentrantReadWriteLock 的读锁和写锁的区别是什么?
  2. ReentrantReadWriteLock 是如何实现公平性和非公平性的?
  3. ReentrantReadWriteLock 的内部结构是怎样的?
  4. ReentrantReadWriteLock 如何实现重入特性?
  5. ReentrantReadWriteLock 的降级和升级是什么?
  6. 什么时候应该使用 ReentrantReadWriteLock 而不是 ReentrantLock?
  7. ReentrantReadWriteLock 的 tryLock 方法有什么作用?
  8. ReentrantReadWriteLock 的写锁和读锁可以同时持有吗?

2. 从使用说起

2.1 基本使用

import java.util.concurrent.locks.ReentrantReadWriteLock;public class BasicUsage {private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();private int value;public void write(int newValue) {lock.writeLock().lock();try {value = newValue;} finally {lock.writeLock().unlock();}}public int read() {lock.readLock().lock();try {return value;} finally {lock.readLock().unlock();}}public static void main(String[] args) {BasicUsage example = new BasicUsage();// Write operationexample.write(42);// Read operationSystem.out.println("Value: " + example.read());}
}

2.2 公平锁和非公平锁

import java.util.concurrent.locks.ReentrantReadWriteLock;public class FairnessExample {private final ReentrantReadWriteLock fairLock = new ReentrantReadWriteLock(true); // 公平锁private final ReentrantReadWriteLock unfairLock = new ReentrantReadWriteLock(false); // 非公平锁public void useFairLock() {fairLock.writeLock().lock();try {// Critical section} finally {fairLock.writeLock().unlock();}}public void useUnfairLock() {unfairLock.writeLock().lock();try {// Critical section} finally {unfairLock.writeLock().unlock();}}
}

2.3 锁降级

import java.util.concurrent.locks.ReentrantReadWriteLock;public class LockDowngrade {private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();private int value;public void writeAndDowngrade(int newValue) {lock.writeLock().lock();try {value = newValue;lock.readLock().lock(); // 获取读锁} finally {lock.writeLock().unlock(); // 释放写锁}try {// 现在持有读锁,可以进行读操作System.out.println("Value after downgrade: " + value);} finally {lock.readLock().unlock(); // 释放读锁}}public static void main(String[] args) {LockDowngrade example = new LockDowngrade();example.writeAndDowngrade(42);}
}

2.4 使用 tryLock

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class TryLockExample {private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();private int value;public boolean tryWrite(int newValue, long timeout, TimeUnit unit) throws InterruptedException {if (lock.writeLock().tryLock(timeout, unit)) {try {value = newValue;return true;} finally {lock.writeLock().unlock();}} else {return false; // 未能获取写锁}}public boolean tryRead(long timeout, TimeUnit unit) throws InterruptedException {if (lock.readLock().tryLock(timeout, unit)) {try {System.out.println("Value: " + value);return true;} finally {lock.readLock().unlock();}} else {return false; // 未能获取读锁}}public static void main(String[] args) throws InterruptedException {TryLockExample example = new TryLockExample();if (example.tryWrite(42, 1, TimeUnit.SECONDS)) {System.out.println("Write successful");} else {System.out.println("Failed to acquire write lock");}if (example.tryRead(1, TimeUnit.SECONDS)) {System.out.println("Read successful");} else {System.out.println("Failed to acquire read lock");}}
}

2.5 读多写少的场景

import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadMostly {private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();private int value;public void write(int newValue) {lock.writeLock().lock();try {value = newValue;} finally {lock.writeLock().unlock();}}public int read() {lock.readLock().lock();try {return value;} finally {lock.readLock().unlock();}}public static void main(String[] args) {ReadMostly example = new ReadMostly();// 模拟多个读线程for (int i = 0; i < 10; i++) {new Thread(() -> {System.out.println("Read value: " + example.read());}).start();}// 模拟写线程new Thread(() -> {example.write(42);System.out.println("Wrote value: 42");}).start();}
}

3. 构造方法

3.1 无参构造

public ReentrantReadWriteLock() {this(false);}

3.2 有惨构造

 public ReentrantReadWriteLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();readerLock = new ReadLock(this);writerLock = new WriteLock(this);}

3.2.1 构造函数参数

fair: 一个布尔值,指示锁是否应该是公平的。如果为 true,则锁是公平的;如果为 false,则锁是非公平的。

3.2.2 内部成员

  • sync: 是一个 AbstractQueuedSynchronizer 的子类,用于实现锁的核心同步机制。根据 fair 参数的值,可以是 FairSync 或 NonfairSync。
  • readerLock: 是一个 ReadLock 对象,表示读锁。
  • writerLock: 是一个 WriteLock 对象,表示写锁。

3.2.3 公平锁与非公平锁

  • 公平锁(FairSync): 公平锁通过内部队列来保证锁的获取顺序,先到达的线程先获取锁,避免线程饥饿。
  • 非公平锁(NonfairSync): 非公平锁不保证锁的获取顺序,可能会插队获取锁,从而提高吞吐量,但可能导致线程饥饿。

3.2.4 ReentrantReadWriteLock 的内部类

ReentrantReadWriteLock 有两个内部类 ReadLock 和 WriteLock,分别表示读锁和写锁。
ReadLock

public static class ReadLock implements Lock, java.io.Serializable {private final Sync sync;protected ReadLock(ReentrantReadWriteLock lock) {sync = lock.sync;}public void lock() {sync.acquireShared(1);}public void unlock() {sync.releaseShared(1);}// 其他方法省略
}
  • ReadLock 的构造函数接受一个 ReentrantReadWriteLock 对象,并从中获取 sync。
  • lock 方法获取读锁,调用 sync 的 acquireShared 方法。
  • unlock 方法释放读锁,调用 sync 的 releaseShared 方法。

WriteLock

public static class WriteLock implements Lock, java.io.Serializable {private final Sync sync;protected WriteLock(ReentrantReadWriteLock lock) {sync = lock.sync;}public void lock() {sync.acquire(1);}public void unlock() {sync.release(1);}// 其他方法省略
}
  • WriteLock 的构造函数接受一个 ReentrantReadWriteLock 对象,并从中获取 sync。
  • lock 方法获取写锁,调用 sync 的 acquire 方法。
  • unlock 方法释放写锁,调用 sync 的 release 方法。

3.2.5 Sync 类

Sync 是 AbstractQueuedSynchronizer 的子类,负责管理锁的状态和队列。根据公平性策略,它有两个实现类:FairSync 和 NonfairSync。
FairSync

static final class FairSync extends Sync {protected final boolean tryAcquire(int acquires) {// 公平锁的获取逻辑}protected final int tryAcquireShared(int unused) {// 公平锁的共享获取逻辑}
}

FairSync 实现了公平锁的获取和释放逻辑。

NonfairSync

static final class NonfairSync extends Sync {protected final boolean tryAcquire(int acquires) {// 非公平锁的获取逻辑}protected final int tryAcquireShared(int unused) {// 非公平锁的共享获取逻辑}
}

NonfairSync 实现了非公平锁的获取和释放逻辑。

4. ReentrantReadWriteLock 的读锁和写锁的区别是什么?

4.1 读锁和写锁的基本区别

  • 读锁 (ReadLock):允许多个线程同时获取,只要没有线程持有写锁。读锁是共享锁。
  • 写锁 (WriteLock):是独占锁,只有一个线程可以持有。当一个线程持有写锁时,其他线程的读锁和写锁请求都会被阻塞。

4.2 代码示例

以下是一个简单的例子,展示了读锁和写锁的不同行为。

import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockExample {private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();private int value;// 写操作:独占锁public void write(int newValue) {writeLock.lock();try {System.out.println(Thread.currentThread().getName() + " acquired write lock");value = newValue;try {Thread.sleep(1000); // 模拟写操作耗时} catch (InterruptedException e) {Thread.currentThread().interrupt();}} finally {System.out.println(Thread.currentThread().getName() + " released write lock");writeLock.unlock();}}// 读操作:共享锁public int read() {readLock.lock();try {System.out.println(Thread.currentThread().getName() + " acquired read lock");try {Thread.sleep(1000); // 模拟读操作耗时} catch (InterruptedException e) {Thread.currentThread().interrupt();}return value;} finally {System.out.println(Thread.currentThread().getName() + " released read lock");readLock.unlock();}}public static void main(String[] args) {ReadWriteLockExample example = new ReadWriteLockExample();// 启动多个读线程for (int i = 0; i < 3; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + " read value: " + example.read());}, "ReadThread-" + i).start();}// 启动一个写线程new Thread(() -> {example.write(42);}, "WriteThread").start();}
}

4.2.1 运行结果

ReadThread-0 acquired read lock
ReadThread-1 acquired read lock
ReadThread-2 acquired read lock
WriteThread acquired write lock
ReadThread-0 released read lock
ReadThread-0 read value: 0
ReadThread-1 released read lock
ReadThread-1 read value: 0
ReadThread-2 released read lock
ReadThread-2 read value: 0
WriteThread released write lock

4.2.2 读锁

  • 多个读线程可以同时获取读锁,不会相互阻塞。
  • 在上面的例子中,ReadThread-0、ReadThread-1 和 ReadThread-2 可以同时获取读锁并执行读操作。

4.2.3 写锁

  • 写锁是独占锁,只有一个线程可以持有写锁。
  • 当一个线程持有写锁时,其他线程的读锁和写锁请求都会被阻塞。
  • 在上面的例子中,WriteThread 获取写锁后,所有的读线程和其他写线程都会被阻塞,直到写锁被释放。

4.3 读写锁的互斥关系

  • 当一个线程持有写锁时,其他线程不能获取读锁或写锁。
  • 当一个或多个线程持有读锁时,其他线程不能获取写锁,但可以获取读锁。

4.4 读锁和写锁的使用场景

  • 读锁:适用于读多写少的场景,多个读线程可以同时读取共享资源,提高并发性能。
  • 写锁:适用于需要修改共享资源的场景,确保修改操作的原子性和线程安全。

5. ReentrantReadWriteLock 如何实现重入特性?

ReentrantReadWriteLock 的重入特性是通过内部的计数器和线程记录来实现的。每个锁(读锁和写锁)都有一个计数器,用于记录当前线程持有锁的次数。
以下是一个展示重入特性的代码示例:

import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReentrantReadWriteLockExample {private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();private int value;// 写操作:独占锁,支持重入public void write(int newValue) {writeLock.lock();try {System.out.println(Thread.currentThread().getName() + " acquired write lock");value = newValue;nestedWrite(); // 调用重入方法} finally {System.out.println(Thread.currentThread().getName() + " released write lock");writeLock.unlock();}}// 读操作:共享锁,支持重入public int read() {readLock.lock();try {System.out.println(Thread.currentThread().getName() + " acquired read lock");nestedRead(); // 调用重入方法return value;} finally {System.out.println(Thread.currentThread().getName() + " released read lock");readLock.unlock();}}// 嵌套的写操作,用于演示重入private void nestedWrite() {writeLock.lock();try {System.out.println(Thread.currentThread().getName() + " re-acquired write lock in nestedWrite");} finally {writeLock.unlock();}}// 嵌套的读操作,用于演示重入private void nestedRead() {readLock.lock();try {System.out.println(Thread.currentThread().getName() + " re-acquired read lock in nestedRead");} finally {readLock.unlock();}}public static void main(String[] args) {ReentrantReadWriteLockExample example = new ReentrantReadWriteLockExample();// 启动一个写线程new Thread(() -> {example.write(42);}, "WriteThread").start();// 启动一个读线程new Thread(() -> {System.out.println(Thread.currentThread().getName() + " read value: " + example.read());}, "ReadThread").start();}
}

5.1 运行结果

WriteThread acquired write lock
WriteThread re-acquired write lock in nestedWrite
WriteThread released write lock
ReadThread acquired read lock
ReadThread re-acquired read lock in nestedRead
ReadThread released read lock
ReadThread read value: 42

5.2 代码解析

5.2.1 写锁的重入

  • 在 write 方法中,线程首先获取写锁,然后调用 nestedWrite 方法
  • 在 nestedWrite 方法中,线程再次获取写锁,这就是写锁的重入
  • 由于是同一个线程持有锁,所以不会被阻塞

5.2.2 读锁的重入

  • 在 read 方法中,线程首先获取读锁,然后调用 nestedRead 方法。
  • 在 nestedRead 方法中,线程再次获取读锁,这就是读锁的重入。
  • 由于是同一个线程持有锁,所以不会被阻塞。

5.3 内部实现

5.3.1 计数器

  • 每个锁(读锁和写锁)都有一个计数器,用于记录当前线程持有锁的次数。
  • 当同一个线程再次获取锁时,计数器增加;当线程释放锁时,计数器减少。

5.3.2 线程记录

  • 锁内部会记录当前持有锁的线程。
  • 当一个线程请求锁时,会检查当前持有锁的线程是否是自己,如果是,则允许重入。

系列文章

1.JDK源码阅读之环境搭建

2.JDK源码阅读之目录介绍

3.jdk源码阅读之ArrayList(上)

4.jdk源码阅读之ArrayList(下)

5.jdk源码阅读之HashMap

6.jdk源码阅读之HashMap(下)

7.jdk源码阅读之ConcurrentHashMap(上)

8.jdk源码阅读之ConcurrentHashMap(下)

9.jdk源码阅读之ThreadLocal

10.jdk源码阅读之ReentrantLock

11.jdk源码阅读之CountDownLatch

12.jdk源码阅读之CyclicBarrier

13.jdk源码阅读之Semaphore

14.jdk源码阅读之线程池(上)

15.jdk源码阅读之线程池(下)

16.jdk源码阅读之ArrayBlockingQueue

17.jdk源码阅读之LinkedBlockingQueue

18.jdk源码阅读之CopyOnWriteArrayList

19.jdk源码阅读之FutureTask

20.jdk源码阅读之CompletableFuture

21.jdk源码阅读之AtomicLong

22.jdk源码阅读之Thread(上)

23.jdk源码阅读之Thread(下)

24.jdk源码阅读之ExecutorService

25.jdk源码阅读之Executors

26.jdk源码阅读之ConcurrentLinkedQueue

27.jdk源码阅读之ConcurrentLinkedDeque

28.jdk源码阅读之CopyOnWriteArraySet

29.jdk源码阅读之Exchanger

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Tooltip 文字提示

在偶然维护前端开发时,遇到页面列表中某个字段内容太长,且该字段使用了组件显示,导致不能使用纯文本得那个省略号代替显示得css样式效果,如下 所以只能另辟溪路了, 1、最开始想到是使用横向滚动得效果来实现,但是实现后,感觉还是不太理想,因为用户注意不到你这里有滚动…

【基础篇】Docker 容器操作 FOUR

嘿&#xff0c;小伙伴们&#xff01;我是小竹笋&#xff0c;一名热爱创作的工程师。在上一篇文章中&#xff0c;我们探讨了 Docker 镜像管理的相关知识。今天&#xff0c;让我们一起深入了解一下 Docker 容器的操作吧&#xff01; &#x1f4e6; 运行、停止和删除容器 Docker…

一个私有化的中文笔记工具个人知识库,极空间Docker部署中文版『Trilium Notes』

一个私有化的中文笔记工具&个人知识库&#xff0c;极空间Docker部署中文版『Trilium Notes』 哈喽小伙伴们好&#xff0c;我是Stark-C~ 最近被很多小伙伴问到NAS上的笔记工具&#xff0c;虽说之前也出过Memos&#xff0c;刚开始用起来还不错&#xff0c;但是用了一段时间…

【JKI SMO】框架讲解(六)

接JKI SMO 框架讲解&#xff08;五&#xff09;&#xff0c;现在对代码进行一个扩展&#xff0c;当前代码仅有一路电压采集&#xff0c;现在需要扩展一路电流采集通道。 下面是对应的步骤&#xff1a; 1.打开项目&#xff0c;在工具里打开SMO Editor。 2.之前创建的SMO会自动加…

快速收集地图商户信息_百度|高德|腾讯|google

数字化营销中企业名录和商家电话号码的采集已成为营销人员日常工作的首要一环。地图平台以其海量的商家信息和实时更新的特性&#xff0c;成为我们获取数据的宝贵渠道。如何快速利用百度、高德、腾讯这三大地图平台高效采集商家联系方式是每个营销人员的必备技能。 我们整理了…

记录某次“有趣的“挖矿木马排查

挖矿木马是什么&#xff1f; 挖矿木马是一种恶意软件&#xff0c;它在用户不知情或未经同意的情况下&#xff0c;利用受害者的计算机资源进行加密货币挖矿。这类软件通过执行大量运算来挖掘数字货币&#xff0c;如比特币或门罗币等。挖矿木马通常通过漏洞利用、弱口令爆破或非…

微软蓝屏事件:全球网络安全与系统稳定性的警示

文章目录 每日一句正能量前言探讨软件更新流程中的风险管理和质量控制机制软件更新的风险风险管理策略质量控制措施测试流程缺陷识别实施质量控制结论 提供预防类似大规模故障的最佳方案或应急响应对策设计冗余系统实施灾难恢复计划建立高可用架构应急响应对策利用自动化工具和…

豆瓣9.9分!大名鼎鼎的深度学习入门书“鱼书”更新第二版了!带你深刻理解神经网络!

大家应该都知道这本非常有名的著作吧&#xff1a;《深度学习入门&#xff0c;基于python的理论与实现》。 这是更新的第二版&#xff0c;是由日本作者斋藤康毅所著的一本关于深度学习的书籍。这本书的出版社是人民邮电出版社&#xff0c;出品方是图灵教育。这本书的原名是“ゼロ…

vue给数组对象赋值改变对象里面的数据,数据没有更新this.$set

替换数组startTime的值&#xff1a; 原数据 this.serviceTimeList.push({serviceTimeName: 服务时间段,startTime: this.startTime,endTime: this.endTime,currentDateStart: this.currentDate,currentDateEnd: this.currentDate}) this.$set(this.array, index, newValue); …

Day12--Servlet实现前后端交互(案例:学生信息管理系统登录页面)

&#xff08;在一个完整的项目架构中&#xff0c;servlet的角色和位置&#xff09; Servlet、GenericServlet和HttpServlet三者之间的关系是Java Web开发中的一个重要概念&#xff0c;它们共同构成了基于Java的服务器端程序的基础。以下是具体分析&#xff1a; 1. Servlet接口…

AI技术革命对未来就业的影响

一、引言 AI技术革命带来的就业结构变化是双面的&#xff0c;既存在着挑战也蕴含着机遇。过去的几次技术革命例如工业革命和信息技术革命都对就业结构产生了深刻的影响&#xff0c;使一些工作消失&#xff0c;也催生了许多新的工作类型。当前和未来的AI技术发展也必将推动类似…

【HadoopShuffle原理剖析】基础篇二

Shuffle原理剖析 Shuffle&#xff0c;是指对Map输出结果进行分区、排序、合并等处理并交给Reduce的过程。分为Map端的操作和Reduce端的操作。 Shuffle过程 Map端的Shuffle Map的输出结果首先被缓存到内存&#xff0c;当缓存区容量到达80%&#xff08;缓冲区默认100MB&#xff…

基于FPGA的数字信号处理(20)--半加器和全减器

1、前言 既然有半加器和全加器&#xff0c;那自然也有半减器和全减器了。尽管在电路中减法的实现基本都是 补码 加法 的形式&#xff0c;但是正所谓技多不压身&#xff0c;了解一下半减器和全减器还是有一定作用的&#xff0c;至少能扩宽知识面嘛。 2、半减器 最简单的减法器叫…

RFID分体式天线如何选购?

分体式读写器天线具有可与RFID读写器分离部署&#xff0c;在远距离读写、集成应用等场景下拥有十分广泛的应用。下面我们就跟大家一起来了解一下&#xff0c;分体式天线选购的要点有哪些? 分体式天线的选购要点主要包括以下几个方面&#xff1a; 一、明确使用需求 应用场景…

机器学习(五) -- 无监督学习(2) --降维2

系列文章目录及链接 上篇&#xff1a;机器学习&#xff08;五&#xff09; -- 无监督学习&#xff08;2&#xff09; --降维1 下篇&#xff1a; 前言 tips&#xff1a;标题前有“***”的内容为补充内容&#xff0c;是给好奇心重的宝宝看的&#xff0c;可自行跳过。文章内容被…

仪器内部压力不稳定的原因分析

仪器的液路压力波动可能由多种原因引起&#xff0c;具体分析如下&#xff1a; 气泡的影响&#xff1a; 流动相未平衡或柱箱温度不稳定时&#xff0c;容易在色谱柱内产生气泡。泵作用下&#xff0c;流动相中的空气可能会分离出来&#xff0c;留在泵体内排不出去。使用梯度程序时…

vmware 设置ip

要用xshell连接vmware虚拟机&#xff0c;要指定虚拟机的ip地址。 进入虚拟机&#xff0c;用ifconfig命令&#xff0c;看下ip 试了下连接192.168.122.1 连接不上&#xff0c;需要重新设置一个。 1&#xff0c;查看电脑的IP地址 winr,输入cmd&#xff0c;再输入ipconfig 看下本…

Java刷题: 丑数判断

题目 丑数 就是只包含质因数 2、3 和 5 的正整数。 给你一个整数 n &#xff0c;请你判断 n 是否为 丑数 。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 解题思路 我觉得刷题是为了扩宽思考的广度。看到这题的时候&#xff0c;我的大脑是发懵的…

letcode - string

翻转字符串 344. 反转字符串 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/reverse-string/ class Solution { public:void reverseString(vector<char>& s) {reverse(s.begin(),s.end());//直接上逆置接口} }; 函数签名: void reverseStr…

redis的代码开发

redis是什么? 前提:官网地址https://redis.io 1.Redis是一个开源的,key,value格式的,内存型数据结构存储系统;它可用作数据库、缓存和消息中间件。 value支持多种类型的数据结构如strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglo…