当前位置: 首页 > news >正文

JUC:读写锁

无锁 => 独占锁 => 读写锁 => 邮戳锁

ReentrantLock, ReentrantReadWriteLock, StampedLock

4.12.1 面试题

  • Java有哪些锁?
  • 对于读写锁,锁饥饿问题是什么?
  • 有没有比读写锁更快的锁?邮戳锁
  • StampedLock知道码?(邮戳锁/票据锁)
  • ReentrantReadWriteLock的锁降级机制是什么?

4.12.2 ReentrantReadWriteLock

读写锁的定义:一个资源能够被多个读线程访问,或者被一个写线程访问,但是不能同时存在读写线程。

class MyResource{Map<String,String> map = new HashMap<>();// Lock lock = new ReentrantLock();ReadWriteLock readWriteLock = new ReentrantReadWriteLock();public void write(String key, String value) throws InterruptedException {// lock.lock();readWriteLock.writeLock().lock();try {System.out.println("正在写入");map.put(key, value);TimeUnit.MILLISECONDS.sleep(50);System.out.println("完成写入");}finally {//lock.unlock();readWriteLock.writeLock().unlock();}}public void read(String key) throws InterruptedException {//lock.lock();readWriteLock.readLock().lock();try {System.out.println("正在读取");map.get(key);TimeUnit.MILLISECONDS.sleep(5000);System.out.println("完成读取");}finally {//lock.unlock();readWriteLock.readLock().unlock();}}

ReentrantReadWriteLock锁降级:将写锁降级为读锁,反之叫锁升级

  • 一个线程锁降级流程
    • 获取
    • 获取
    • 释放

如果先获取读锁,并且没有释放时,线程是无法获取写锁的。因此ReentrantReadWriteLock无法完成锁升级

ReadWriteLock readWriteLock = new ReentrantReadWriteLock();readWriteLock.writeLock().lock(); // 1. 获取写锁
// 写锁状态
readWriteLock.readLock().lock(); // 2. 获取读锁
readWriteLock.writeLock().unlock();// 3. 释放写锁// 锁降级之后为读锁状态readWriteLock.readLock().unlock(); 

统一线程先获取写锁再去获取读锁,相当于重入。

4.12.3 StampedLock

  • 定义
    • StampedLock是JDK1.8新增的一个读写锁,是对ReentrantReadWriteLock的优化。
    • 邮戳锁, 也成为票据锁
    • stamp(戳记,long类型):代表了锁的状态。当stamp返回0时,表示线程获取锁失败,并且,当释放锁或者转换锁时,都要传入最初获取的stamp值。
  • 作用
    • 解决锁饥饿:对于短的只读代码块,使用乐观模式通常可以减少争用并提高吞吐量。
    • 锁降

锁饥饿案例:假如当前有1000个线程,999个在进行读操作,1个在进行写操作。有可能999个读操作长期获取锁,导致1个写操作长时间获取不到锁。导致锁饥饿

ReentrantReadWriteLock 和 StampedLock对比:

ReentrantReadWVriteLock

  • 允许多个线程同时读,但是只允许一个线程写,在线程获取到写锁的时候,其他写操作和读操作都会处于阻塞状态。

  • 读锁和写锁也是互斥的,所以在读的时候是不允许写的,读写锁比传统的synchronized速度要快很多,原因就是在于ReentrantReadWriteLock文持读并发,读读可以共享

StampedLock

  • ReentrantReadWriteLock的读锁被占用的时候,其他线程尝武获取写锁的时候会被阻塞。
  • 但是,StampedLock采取乐观获取锁后,其他线程尝试获取写锁时不会被阳塞,这其实是对读锁的优化,所以,在获取乐观读锁后,还需要对结果进行校验。

对于短的只读代码块,使用乐观模式通常可以减少争用并提高吞吐量。

(1)特点

  • 所有获取锁的方法,都返回一个邮戳StampStamp为零表示获取失败,其余都表示成功;

  • 所有释放锁的方法都需要一个邮戳Stamp,这个Stamp必须是和成功获取锁时得到的Stamp一致;

  • StampedLock是不可重入的,危险 !!!(如果一个线程已经持有了写锁,再去获取写锁的话就会造成死锁)

  • StampedLock有三种访问模式

    • Reading(悲观读模式):和ReentrantReadWriteLock.readLock().lock()一样
    • Writing(悲观写模式):和ReentrantReadWriteLock.writeLock().lock()一样
    • Optimistic Reading(乐观读模式): 无锁机制,类似于数据库中的乐观锁,支持读写并发,乐观认为读取时没有人修改,如果被修改再升级为悲观读模式

乐观读模式:仅当锁定当前未处于写入模式时,方法tryOptimisticead()才返回非零戳记。如果自获得给定标记以来未在写入模式下获取锁定,则方法validate(long)返回tue。这种模式可以被认为是读锁的极弱版本,可以随时被作者,坏。对短的只读代码段使用乐观模式通常可以减少争,用并提高吞吐量。但是,它的使用本质上是脆弱的。乐观读取部分应该只读取字段并将它们保存在局部变量中,以便以后在验证后使用。在乐观摸式下读取的字段可能非常不一致,因此仅在您熟采数据表示以拾查一致性和/或重复调用方法validate()。例如,在首次读取对象或数组引用,然后访问其中一个字段,元素或方法时,通常需要执行此类步。

(2)代码演示

// writelong writeStamp = stampedLock.writeLock();
//业务代码
stampedLock.unlock(writeStamp);// read
long readStamp = stampedLock.readLock();
stampedLock.unlock(writeStamp);
  • 普通读写
public void write(){long stamp = stampedLock.writeLock();System.out.println("WW 写线程准备修改");try{num = num + 1;}finally {stampedLock.unlockWrite(stamp);}System.out.println("WW 写线程修改结束");
}/*** 悲观读*/
public void read(){long stamp = stampedLock.readLock();System.out.println("RR 读线程准备读取");try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}try {System.out.println("读线程读取" + num);}finally {stampedLock.unlockRead(stamp);}System.out.println("RR 读线程完成读取");
}
  • 乐观读
/*** 乐观读*/
public void optimisticRead(){long stamp = stampedLock.tryOptimisticRead();int result = num;//故意间隔4秒 乐观认为读取中没有其他线程修改过number值,具体靠判断System.out.println("4s前stampedLock.validate方法(true无修改,false有修改): " + stampedLock.validate(stamp));for (int i = 0; i < 4; i++) {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("OR 正在读取 " + i + "::" + num);System.out.println("OR STAMP Validate标记:" + stampedLock.validate(stamp));}if (!stampedLock.validate(stamp)) { // 有写操作,可以对其进行手动升级System.out.println("有人修改");long readStamp = stampedLock.readLock();try{System.out.println("OR RR 升级悲观读");result = num;System.out.println("OR RR 悲观读后 result " + result);}finally {stampedLock.unlockRead(readStamp);}}System.out.println("OR 最终值 " + result);
}

(3)缺点

  • StampedLock不支持重入,没有Reentrant开头。读写锁原理上都不是可重入锁 ,读锁是伪重入,需要多次释放
  • StampedLock的悲观读锁和写锁都不支持条件变量(Condition),
  • 使用StampedLock一定不要调用中断操作,即不要调用interrupt()方法。
http://www.zskr.cn/news/14688.html

相关文章:

  • 2025 年点胶机厂家 TOP 企业推荐排行榜,自动 / 果冻胶 / 无痕内衣 / 烫钻 / 珠宝热熔胶 / 水钻热熔胶 / 亮片热熔胶 / 金葱粉热熔胶点胶机推荐这十家公司!
  • 2025换热器厂家最新推荐白皮书,不锈钢 / 钛 / 哈氏合金 / 碳钢 / 衬四氟 / 列管式 / 螺旋板 / 管壳式 / 缠绕式 / 复合材料换热器公司推荐!
  • IELTS-G Writing Task1 informal letters
  • 2025 年 AI 教育培训机构推荐及选择指南:企业 AI 教育培训 / AI + 教育 / AI 教育线下机构 / AI 企业教育培训机构 / AIGC 教育培训推荐这五家公司!
  • 社区互助养老框架|基于java和小程序的社区互助养老系统小程序设计与实现(源码+数据库+文档)
  • oppoR9m刷Linux系统: 电脑安装驱动工具
  • oppoR9m刷Linux系统: 手机获取系统root权限
  • 国庆集训模拟赛记录
  • oppoR9m刷Linux系统: 工具、软件下载
  • 实用指南:HTTP(web缓存与历史迭代)
  • 信创PC收藏网址
  • 2025 年离心泵厂家 TOP 企业品牌推荐排行榜!化工,卧式多级,不锈钢,立式,氟塑料,管道,衬氟,耐腐蚀离心泵推荐这十家公司!
  • 面试复习题---Flutter 资深专家 - 详解
  • Notion 3.0 发布会深度解析:AI原生时代的知识工作重新定义 - 实践
  • 完整教程:MYSQL —— 约束和多表查询
  • 2025板材厂家 TOP 企业品牌推荐排行榜,环保 / 密度 / 净化 / 零醛添加 / 装修 / 生态板 / 指接板 / 直拼板 / PET 实木板材公司推荐!
  • 《电路基础》第三章学习笔记
  • 【C#】以 BlockingCollection 为核心的多相机 YOLO 检测任务处理框架 - 指南
  • 上手 cpp-httplib:轻量级 C++ HTTP 库的安装与实战指南 - 教程
  • 题解:P9868 [NOIP2023] 词典
  • AtCoder Beginner Contest 425
  • MaopaiJD Esp8266 代码
  • Ynoi Easy Round 2015 学习笔记
  • 深入解析:5. Prompt 提示词
  • 【自学笔记】Redis 飞快入门
  • 实用指南:K8s日志架构:Sidecar容器实践指南
  • 详细介绍:开源 java android app 开发(十七)封库--混淆源码
  • Meta基础设施演进与AI技术革命
  • 完整教程:Spring AI整合聊天模型DeepSeek
  • 2025 年焚烧炉厂家 TOP 企业品牌推荐排行榜!权威甄选实力与口碑俱佳的江苏焚烧炉 / 无锡焚烧炉推荐这十家公司!