深入解析 ReentrantReadWriteLock 和 StampedLock 的源码

news/2025/1/8 8:32:47/
引言

在高并发环境中,读写锁(Read-Write Lock)是一种非常重要的同步工具。它们允许多个线程同时进行读操作,但在有写操作时确保独占访问。Java 提供了 ReentrantReadWriteLockStampedLock 两种读写锁实现,分别适用于不同的场景。本文将详细解析这两种读写锁的内部机制。

一、ReentrantReadWriteLock 源码解析
1.1 结构概述

ReentrantReadWriteLock 是一个可重入的读写锁,它维护了一对相关联的锁:一个用于只读操作(读锁),另一个用于写入操作(写锁)。读锁是共享锁,允许多个线程同时持有;而写锁是独占锁,一次只能有一个线程持有。此外,ReentrantReadWriteLock 支持公平性和非公平性选择,并且具备锁降级功能。

1.2 状态表示

为了管理读锁和写锁的状态,ReentrantReadWriteLock 使用了一个整数 state 来存储信息。这个整数被分为两部分:

  • 高 16 位用来表示读状态。
  • 低 16 位用来表示写状态。

通过位运算可以快速获取或设置读写状态。例如,sharedCount(int c) 方法用于获取当前持有的读锁数量,而 exclusiveCount(int c) 方法则用于获取写锁的数量。

java">static final int SHARED_SHIFT   = 16;
static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
1.3 锁的获取与释放
  • 读锁获取:当尝试获取读锁时,首先检查是否有其他线程正在写入。如果没有,则增加读计数器并返回成功。如果有写请求但调用线程已经持有了写锁,则也可以获取读锁。

  • 写锁获取:获取写锁时会检查是否已经有其他线程持有读锁或写锁。如果是非公平模式,即使队列中有等待者也会尝试直接获取锁;如果是公平模式,则必须按照顺序排队。

  • 锁的释放:无论是读锁还是写锁,在释放时都会减少相应的计数器。当所有类型的锁都被完全释放后,才会唤醒下一个等待的线程。

1.4 锁降级

锁降级指的是从写锁转换为读锁的过程。如果一个线程先获得了写锁,然后在同一时间内又获得了读锁,最后再释放写锁,那么此时该线程就只剩下了读锁。这有助于提高性能,因为在某些情况下,不需要立即释放写锁就可以继续执行后续的读操作。

二、StampedLock 源码解析
2.1 特点介绍

StampedLock 是 Java 8 引入的一种新的读写锁实现,它的主要特点是提供了乐观读模式。这意味着读取数据时不加锁,只有在检测到数据可能已经被修改的情况下才会上悲观读锁。这种方式极大地提高了读操作的吞吐量,减少了线程饥饿的问题。

2.2 内部机制

StampedLock 不像 ReentrantReadWriteLock 那样依赖于 AQS 框架,而是采用了一种更轻量级的设计。它使用“邮戳”(stamp)来跟踪锁的状态变化。每次获取锁时都会返回一个唯一的 stamp 值,之后可以通过 validate() 方法验证该 stamp 是否仍然有效。

  • 写锁:类似于 ReentrantReadWriteLock 的写锁行为,保证同一时刻只有一个线程能够进行写操作。

  • 悲观读锁:允许多个线程同时持有读锁,但与写锁互斥。

  • 乐观读:这是 StampedLock 的核心特性之一。在线程试图读取数据之前,它会尝试以乐观的方式获取一个 stamp。如果在此期间没有其他线程修改数据,则可以直接使用读取的数据;否则需要重新上悲观读锁并重新读取数据。

2.3 示例代码

下面是一个简单的例子展示了如何使用 StampedLock 进行乐观读:

java">public class Point {private final StampedLock stampedLock = new StampedLock();private double x, y;public void move(double deltaX, double deltaY) {long stamp = stampedLock.writeLock();try {x += deltaX;y += deltaY;} finally {stampedLock.unlockWrite(stamp);}}public double distanceFromOrigin() {long stamp = stampedLock.tryOptimisticRead();double currentX, currentY;currentX = x;if (!stampedLock.validate(stamp)) {stamp = stampedLock.readLock();try {currentX = x;currentY = y;} finally {stampedLock.unlockRead(stamp);}} else {currentY = y;}return Math.sqrt(currentX * currentX + currentY * currentY);}
}
总结

通过对 ReentrantReadWriteLockStampedLock 的源码解析,我们可以看到两者虽然都实现了读写锁的功能,但在设计哲学和技术细节上有着显著的区别。前者更适合那些需要严格控制线程安全性的场合,而后者则更加关注高性能和高吞吐量的应用场景。理解这些底层原理不仅有助于我们编写高效的并发程序,也能帮助我们在实际开发中做出更好的技术选型决策。


http://www.ppmy.cn/news/1561508.html

相关文章

NLP CH3复习

CH3 3.1 几种损失函数 3.2 激活函数性质 3.3 哪几种激活函数会发生梯度消失 3.4 为什么会梯度消失 3.5 如何解决梯度消失和过拟合 3.6 梯度下降的区别 3.6.1 梯度下降&#xff08;GD&#xff09; 全批量&#xff1a;在每次迭代中使用全部数据来计算损失函数的梯度。计算成本…

最好用的图文识别OCR -- PaddleOCR(2) 提高推理效率(PPOCR模型转ONNX模型进行推理)

在实际推理过程中&#xff0c;使用 PaddleOCR 模型时效率较慢&#xff0c;经测试每张图片的检测与识别平均耗时超过 5 秒&#xff0c;这在需要大规模自动化处理的场景中无法满足需求。为此&#xff0c;我尝试将 PaddleOCR 模型转换为 ONNX 格式进行推理&#xff0c;以提升效率。…

CV-MLLM经典论文解读|OneLLM: One Framework to Align All Modalities with Language

论文标题&#xff1a; OneLLM: One Framework to Align All Modalities with Language OneLLM&#xff1a;一个框架&#xff0c;将所有模态与语言对齐 论文链接&#xff1a; Pink: Unveiling the Power of Referential Comprehension for Multi-modal LLMs论文下载 论文作…

基于SPring Boot的高校就业招聘系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

Ubuntu 、Debian(Kali) 、Centos 命令区别(三剑客)

1、安装 Ubuntu从软件源 安装&#xff1a; apt-get install XXX (包名)Debian(Kali)从软件源 安装&#xff1a; apt-get install xxx (包名) CentOS从软件源 安装&#xff1a; yum install XXX XXX (包名) 2、包的信息 Debian(Kali)从软件源 安装&#xff1a; apt-cache sea…

GitHub的简单操作

引言 今天开始就要开始做项目了&#xff0c;上午是要把git搭好。搭的过程中遇到好多好多的问题。下面就说一下git的简单操作流程。我们是使用的GitHub,下面也就以这个为例了 一、GitHub账号的登录注册 https://github.com/ 通过这个网址可以来到GitHub首页 点击中间绿色的S…

番外篇-CSS3新增特性

CSS3是CSS的第三个版本&#xff0c;引入了许多新的特性和功能。以下是一些CSS3新增的特性&#xff1a; 1. 变换&#xff08;Transform&#xff09;&#xff1a;可以通过旋转、缩放、倾斜和平移等变换操作改变元素的外观和位置。 2. 过渡&#xff08;Transition&#xff09;&a…

【linux系统之redis6】redis的基础命令使用及springboot连接redis

redis的基础命令很多&#xff0c;大部分我们都可以在官网上找到&#xff0c;真的用的时候可以去官网找&#xff0c;不用全部记住这些命令 redis通用的基础命令的使用 代码测试 string类型常见的命令 key值的结构&#xff0c;可以区分不同的需求不同的业务名字 hash类型 创建…