【用 StampedLock 实现高效读写锁】

news2025/1/15 23:50:23

深入理解 StampedLock

1. 它是什么?

StampedLock 是 Java 8 引入的高性能锁,提供了三种锁模式:写锁、悲观读锁和乐观读锁。
与传统的 ReentrantReadWriteLock 相比,StampedLock 更注重性能,特别适合读多写少的场景。

  • 写锁:阻塞所有其他操作(类似独占锁)。
  • 悲观读锁:共享锁,允许多个线程读取,但会阻塞写操作。
  • 乐观读锁:一种非阻塞读操作,允许并发写操作,并在必要时验证数据一致性。

2. 它的使用场景是什么?

  • 读多写少的场景:如缓存、配置数据读取等场景,StampedLock 的乐观读锁可以显著提高性能。
  • 需要快速读锁验证的场景:在乐观读的情况下,可以验证数据的一致性并在必要时降级为悲观读锁。
  • 写锁需要优先处理的场景:避免传统读写锁中写线程因读操作而长期饥饿的问题。

3. 它有哪些 API?

核心 API

方法名称描述
writeLock()获取写锁,返回一个 stamp(锁标识)。
readLock()获取悲观读锁,返回一个 stamp(锁标识)。
tryOptimisticRead()获取乐观读锁,返回一个 stamp,非阻塞,适合快速读取。
unlockWrite(long stamp)释放写锁,需要传入获取锁时返回的 stamp。
unlockRead(long stamp)释放悲观读锁,需要传入获取锁时返回的 stamp。
validate(long stamp)验证乐观读锁期间是否有写操作发生,返回 true 表示数据未被修改。false表示数据已经被修改过

扩展 API

方法名称描述
tryWriteLock()尝试获取写锁,如果未成功立即返回。
tryReadLock()尝试获取读锁,如果未成功立即返回。
tryConvertToWriteLock(long stamp)尝试将当前锁转换为写锁,成功时返回新 stamp,否则返回 0。
tryConvertToReadLock(long stamp)尝试将当前锁转换为读锁,成功时返回新 stamp,否则返回 0。
isWriteLocked()检查当前锁是否有写锁被占用。
isReadLocked()检查当前锁是否有读锁被占用。

4. 它的使用方式

(1)写锁的使用

public void updateValue(double deltaX, double deltaY) {
    long stamp = lock.writeLock();  // 获取写锁
    try {
        x += deltaX;
        y += deltaY;
    } finally {
        lock.unlockWrite(stamp);  // 释放写锁
    }
}

(2)悲观读锁的使用

public double readValue() {
    long stamp = lock.readLock();  // 获取读锁
    try {
        return Math.sqrt(x * x + y * y);
    } finally {
        lock.unlockRead(stamp);  // 释放读锁
    }
}

(3)乐观读锁的使用

public double readValueOptimistically() {
    long stamp = lock.tryOptimisticRead();  // 获取乐观读锁
    double currentX = x, currentY = y;

    if (!lock.validate(stamp)) {  // 验证数据是否一致
        stamp = lock.readLock();  // 如果不一致,降级为悲观读锁
        try {
            currentX = x;
            currentY = y;
        } finally {
            lock.unlockRead(stamp);  // 释放悲观读锁
        }
    }

    return Math.sqrt(currentX * currentX + currentY * currentY);
}

(4)锁升级的使用

public void conditionalUpdate(double deltaX, double deltaY) {
    long stamp = lock.readLock();  // 获取悲观读锁
    try {
        if (x == 0 && y == 0) {  // 检查条件
            stamp = lock.tryConvertToWriteLock(stamp);  // 升级为写锁
            if (stamp == 0L) {  // 如果升级失败
                stamp = lock.writeLock();  // 显式获取写锁
            }
            x += deltaX;
            y += deltaY;
        }
    } finally {
        lock.unlock(stamp);  // 释放锁
    }
}

5. 它有哪些注意事项

(1)StampedLock 不可重入
StampedLock 不支持重入。如果同一线程尝试再次获取锁(无论读锁还是写锁),会导致死锁。

(2)锁释放需要传入正确的 stamp
每次加锁时都会返回一个唯一的 stamp,在释放锁时需要传入对应的 stamp,否则会抛出 IllegalMonitorStateException。

(3)写优先策略
StampedLock 优先满足写锁请求,避免了读写锁可能出现的写线程饥饿问题。

(4)线程安全
StampedLock 是线程安全的,但不支持条件变量(Condition),因此无法直接使用 wait 或 notify。

(5)适用场景
适合 读多写少 的场景。
不适合写频繁的场景,因为写锁的争用会导致性能下降。

总结

StampedLock 是 Java 并发工具库中的一颗“冷门宝石”,它通过乐观读锁提供了高效的非阻塞读机制,同时避免了写线程饥饿的问题。熟悉其 API 和使用场景,能够帮助你在性能敏感的场景中实现更高效的并发控制!

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

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

相关文章

Deep4SNet: deep learning for fake speech classification

Deep4SNet:用于虚假语音分类的深度学习 摘要: 虚假语音是指即使通过人工智能或信号处理技术产生的语音记录。生成虚假录音的方法有"深度语音"和"模仿"。在《深沉的声音》中,录音听起来有点合成,而在《模仿》中…

Docker save load 镜像 tag 为 <none>

一、场景分析 我从 docker hub 上拉了这么一个镜像。 docker pull tomcat:8.5-jre8-alpine 我用 docker save 命令想把它导出成 tar 文件以便拷贝到内网机器上使用。 docker save -o tomcat-8.5-jre8-alpine.tar.gz 镜像ID 当我把这个镜像传到别的机器,并用 dock…

备战蓝桥杯 队列和queue详解

目录 队列的概念 队列的静态实现 总代码 stl的queue 队列算法题 1.队列模板题 2.机器翻译 3.海港 双端队列 队列的概念 和栈一样,队列也是一种访问受限的线性表,它只能在表头位置删除,在表尾位置插入,队列是先进先出&…

工厂物流管理系统方案(二):危险品车辆专用导航系统架构设计深度剖析

本文专为IT架构师、物流技术专家、软件开发工程师及对危险品运输导航技术有深入探索需求的读者撰写,旨在全面解析危险品车辆专用导航系统的架构设计,展现其技术深度与复杂性,为行业同仁提供权威的技术参考与实践指导。如需获取危险品车辆专用…

用 Python 从零开始创建神经网络(十九):真实数据集

真实数据集 引言数据准备数据加载数据预处理数据洗牌批次(Batches)训练(Training)到目前为止的全部代码: 引言 在实践中,深度学习通常涉及庞大的数据集(通常以TB甚至更多为单位)&am…

No.1|Godot|俄罗斯方块复刻|棋盘和初始方块的设置

删掉基础图标新建assets、scenes、scripts文件夹 俄罗斯方块的每种方块都是由四个小方块组成的,很适合放在网格地图中 比如网格地图是宽10列,高20行 要实现网格的对齐和下落 Node2D节点 新建一个Node2D 添加2个TileMapLayer 一个命名为Board&…

蓝桥云客第 5 场 算法季度赛

题目: 2.开赛主题曲【算法赛】 - 蓝桥云课 问题描述 蓝桥杯组委会创作了一首气势磅礴的开赛主题曲,其歌词可用一个仅包含小写字母的字符串 S 表示。S 中的每个字符对应一个音高,音高由字母表顺序决定:a1,b2,...,z26。字母越靠后…

刀客doc:快手的商业化架构为什么又调了?

一、 1月10日,快手商业化及电商事业部进行新一轮的架构调整。作为2025年快手的第一次大调整,变动最大的是负责广告业务的商业化事业部。快手商业化将原来的8个业务中心,现在统合成了5个,行业归拢看上去更加明晰了。 根据自媒体《…

6.2 MySQL时间和日期函数

以前我们就用过now()函数来获得系统时间,用datediff()函数来计算日期相差的天数。我们在计算工龄的时候,让两个日期相减。那么其中的这个now函数返回的就是当前的系统日期和时间。 1. 获取系统时间函数 now()函数,返回的这个日期和时间的格…

mock服务-通过json定义接口自动实现mock服务

go-mock介绍 不管在前端还是后端开发过程中,当我们需要联调其他服务的接口,而这个服务还没法提供调用时,那我们就要用到mock服务,自己按接口文档定义一个临时接口返回指定数据,以供本地开发联调测试。 怎么快速启动一…

sparkSQL练习

1.前期准备 (1)建议先把这两篇文章都看一下吧,然后把这个项目也搞下来 (2)看看这个任务 (3)score.txt student_id,course_code,score 108,3-105,99 105,3-105,88 107,3-105,77 105,3-245,87 1…

CSS | 实现三列布局(两边边定宽 中间自适应,自适应成比)

目录 示例1 (中间自适应 示例2(中间自适应 示例3(中间自适应 示例4 (自适应成比 示例5(左中定宽,右边自适应 示例6(中间自适应 示例7(中间自适应 示例8(中间定宽…

力扣 子集

回溯基础,一题多解,不同的回朔过程。 题目 求子集中,数组的每种元素有选与不选两种状态。因此在使用dfs与回溯时把每一个元素分别进行选与不选的情况考虑即可。可以先用dfs跳过当前元素即不选然后一直深层挖下去,直到挖到最深了即…

网络层协议-----IP协议

目录 1.认识IP地址 2.IP地址的分类 3.子网划分 4.公网IP和私网IP 5.IP协议 6.如何解决IP地址不够用 1.认识IP地址 IP 地址(Internet Protocol Address)是指互联网协议地址。 它是分配给连接到互联网的设备(如计算机、服务器、智能手机…

RocketMQ 知识速览

文章目录 一、消息队列对比二、RocketMQ 基础1. 消息模型2. 技术架构3. 消息类型4. 消费者类型5. 消费者分组和生产者分组 三、RocketMQ 高级1. 如何解决顺序消费和重复消费2. 如何实现分布式事务3. 如何解决消息堆积问题4. 如何保证高性能读写5. 刷盘机制 (topic 模…

C++(类和对象)

C中的类 C中兼容对C语言中struct的所有用法.同时C对struct进行了语法的升级.将struct升级成了类. // c中对于struct的改进: struct Stack {int* a;int top;int capacity; } int main() { Stack s;// 这里可以直接使用Stack进行使用,而不再需要struct关键字了return 0; }注意:…

centos 8 中安装Docker

注:本次样式安装使用的是centos8 操作系统。 1、镜像下载 具体的镜像下载地址各位可以去官网下载,选择适合你们的下载即可! 1、CentOS官方下载地址:https://vault.centos.org/ 2、阿里云开源镜像站下载:centos安装包…

Sui Move:基本概览一

Module (模块) Move 代码被组织成模块, 可以把一个模块看成是区块链上的一个智能合约 可以通过调用这些模块中的函数来与模块进行交互,可以通过事务或其他 Move 代码来实现, 事务将被发送到并由Sui区块链进行处理,一旦执行完成,结果的更改将…

1/13+2

运算符重载 myString.h #ifndef MYSTRING_H #define MYSTRING_H #include <cstring> #include <iostream> using namespace std; class myString {private:char *str; //记录c风格的字符串int size; //记录字符串的实际长度int capacity; …

GD32F470Z外部晶振不起振

亲测&#xff0c;主要的原因是因为系统配置里面选择的晶振&#xff0c;选择内部还是外部的无源晶振。 1.无源晶振 打开startup_gd32f450_470.s这个起始文件。 ​​​​​​​ ​​​​​​​ 找到SystemInit。 跳进去这个函数。 在这个函数里面最底下找到sys…