ReentrantReadWriteLock入门级

ReentrantReadWriteLock之深入源码解析 ReentrantLock是排他锁,在高并发读多写少的场景下,同时还要保证线程安全,如果使用ReentrantLock的效率不是那么好,所以才有了ReentrantReadWriteLock。在讲源码之前,我们对读写锁要有个认知。读读操作是共享的。读写操作是互斥的。写写操作是互斥的。写读操作是互斥的。(写读两个不同线程)单线程获取写锁后,再次获取读锁,可以拿到。(写读操作可以重入)单线程获取读锁后,再次获取写锁,不可以拿到。 阅读详情

是什么

解决线程安全我们用ReentrantLock就可以了,但是对于读多写少的情况下我们继续使用它会有损性能,因为在没有写但有持续读数据的情况下,加锁是没有必要的,所以ReentrantReadWriteLock为了解决这个问题应运而生,它满足这样的规则:读读共享、读写互斥、写写互斥。更进一步的解释,它内部维护了两种锁即写锁和读锁。在没有线程获取写锁的时候,多个线程可以同时获取读锁。如果有线程获取写锁,则其他线程请求获取读锁的时候会阻塞挂起。

怎么用

ReentrantReadWriteLock API 上的使用与ReentrantLock 基本一致。这边我们以读多写少的 场景下来测试对比两者之间的性能 并同时了解ReentrantReadWriteLock 是如何使用的。 具体实现如下:

ReentrantLock性能测试代码

public class ReentrantLockDemo {
    Lock lock = new ReentrantLock();
    private volatile Integer value;
    private static AtomicLong readTime = new AtomicLong();
    private static AtomicLong writeTime = new AtomicLong();

    public void add(Integer value) {
        lock.lock();
        this.value = value;
        lock.unlock();
    }

    public Integer get() {
        try {
            lock.lock();
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return this.value;
        } finally {
            lock.unlock();
        }
    }
}

运行方法

        ReentrantLockDemo demo = new ReentrantLockDemo();
        CyclicBarrier cyclicBarrier = new CyclicBarrier(160);
        CountDownLatch latch = new CountDownLatch(160);
        
        // 10个写线程
        for (Integer i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    cyclicBarrier.await();
                    
                    long start = System.nanoTime();
                    demo.add(4);
                    long duration = System.nanoTime() - start;
                    
                    writeTime.addAndGet(duration);
                    latch.countDown();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }

        // 150 个读线程
        for (int i = 0; i < 150; i++) {
            new Thread(
                    () -> {
                        try {
                            cyclicBarrier.await();
                            
                            long start = System.nanoTime();
                            demo.get();
                            long duration = System.nanoTime() - start;
                            
                            readTime.addAndGet(duration);
                            latch.countDown();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
            ).start();
        }

        latch.await();

        System.out.println("ReentrantLockDemo 平均读耗时:" + String.valueOf(readTime.get() / 150) + "ns");
        System.out.println("ReentrantLockDemo 平均写耗时:" + String.valueOf(writeTime.get() / 10) + "ns");

ReentrantReadWriteLock性能测试代码

public class ReentrantReadWriteLockDemo {
    ReadWriteLock lock = new ReentrantReadWriteLock();
    private Lock readLock = lock.readLock();
    private Lock writeLock = lock.writeLock();

    private static AtomicLong readTime = new AtomicLong();
    private static AtomicLong writeTime = new AtomicLong();
    private volatile Integer value;
    
    public void add(Integer value) {//(4)
        writeLock.lock();
        this.value = value;
        writeLock.unlock();
    }

    public Integer get() {//(5)
        try {
            readLock.lock();
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return this.value;
        } finally {
            readLock.unlock();
        }
    }
}    

运行方法

    ReentrantReadWriteLockDemo demo = new ReentrantReadWriteLockDemo();
        CyclicBarrier cyclicBarrier = new CyclicBarrier(160);// (1)
        CountDownLatch latch = new CountDownLatch(160);//(2)
        // 10个写线程
        for (Integer i = 0; i < 10; i++) {
            new Thread(() -> {


                try {
                    cyclicBarrier.await();
                    
                    long start = System.currentTimeMillis();
                    demo.add(2);
                    long duration = System.currentTimeMillis() - start;
                    
                    writeTime.addAndGet(duration);
                    latch.countDown();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }

        // 150 个读线程
        for (int i = 0; i < 150; i++) {
            new Thread(
                    () -> {
                        try {
                            cyclicBarrier.await();
                            
                            long start = System.currentTimeMillis();
                            demo.get();
                            long duration = System.currentTimeMillis() - start;
                            
                            readTime.addAndGet(duration);
                            latch.countDown();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
            ).start();
        }

        latch.await();//(3)

        System.out.println("ReentrantReadWriteLock 平均读耗时:" + String.valueOf(readTime.get() / 150) + "ns");
        System.out.println("ReentrantReadWriteLock 平均写耗时:" + String.valueOf(writeTime.get() / 10) + "ns");

运行后的性能对比:

ReentrantLockDemo 平均读耗时:37990465770ns
ReentrantLockDemo 平均写耗时:501314688ns

ReentrantReadWriteLock 平均读耗时:1001ns
ReentrantReadWriteLock 平均写耗时:353ns

通过测试结果可知在读多写少的场景下,ReentrantReadWriteLock的性能明显大于 ReentrantLock。两者的性能测试代码基本一致,我们着重讲解ReentrantReadWriteLock的代码。代码(1)处使用了CyclicBarrier 任务栅栏,它保证10个写线程和150读线程同时并发进行操作。代码(2)、(3)处 使用CountDownLatch保证 所有线程执行完毕后,再输出读写的耗时。代码(4)处获取写锁保证同一时刻只有一个线程对数据进行写操作。代码(5)处获取读锁,保证在没有写线程进行时,多个读线程能同时读取资源,不会被阻塞挂起。其中将线程睡眠500毫秒的意义在于模拟读数据耗时的场景,有助于进行两者的性能测试对比。

看到这里大家可能会有此疑问:写数据加锁,能保证不会出现写入脏数据的问题,这无可厚非,但读数据的时候我为什么要加读锁?不加读锁不也是能读到最新的数据么?如果你这样想那就大错特错了,它会造成数据不一致性的问题。什么意思呢?假设某线程需要在 [T1,T10] 时间段才能将事情处理完,在T1时间获取 value 值为 2 ,但在T3时刻另一线程对value进行了更新,导致该线程在T10时刻 获取到的value值与 T1 时刻获取的不一样。(类比于数据库中的不可重复读)

哪里用

对于大数据量并发访问缓存的情景下可以采用。

书写技术文章是一个循序渐进的过程,所以我不能保证每句话、每行代码都是对的,但至少能保证不复制、不粘贴,每篇文章都是自己对技术的认识、细心斟酌总结出来的。乔布斯说:我们在这个星球上的时间都很短,很少有机会去做几件真正伟大的事情,同时要做得好,我必须要趁我还年轻的时候完成这些事。
其实我想说的是,我是一枚程序员,我只想在有限的时间内尽可能去沉淀我这一生中所能沉淀下来的东西


清山绿水始于尘,博学多识贵于勤。

我有酒,你有故事吗?

微信公众号:「Java锦囊」。

欢迎一起谈天说地,聊Java。


基于AQS实现的ReentrantReadWriteLock源代码深入分析 摘要:ReentrantReadWriteLock通过读写分离提升并发性能,适用于读多写少场景。它使用int类型的state变量高16位记录读锁数量(共享模式),低16位记录写锁重入次数(独占模式)。读锁允许多线程并发访问,写锁则互斥独占,支持锁降级(写锁转读锁)但不支持锁升级(读锁转写锁)。源码示例展示了如何通过锁降级确保数据一致性。这种设计通过位运算高效管理读写状态,显著提升了高并发环境下的性能表现。 阅读详情

相关推荐

ReentrantReadWriteLock源码分析

本文深入分析了ReentrantReadWriteLock的实现原理,主要特点包括:1. 通过一对关联的读锁(共享)和写锁(独占)实现;2. 支持锁降级(写锁降为读锁);3. 读读共享、读写互斥、写写互斥的锁策略;4. 可重入特性。源码层面详细剖析了读锁的获取(tryAcquireShared)和释放(tryReleaseShared)流程,包括CAS更新状态、等待队列处理等核心机制。写锁实现与ReentrantLock类似,通过exclusiveCount维护独占性。整体基于AQS框架,在低竞争场景下性能

ywl470812087的博客 782

ReentrantReadWriteLock

ReentrantReadWriteLock底层是基于AbstractQueuedSynchronizer来实现的,所以,ReentrantReadWriteLock的数据结构也依托于AQS的数据结构。只有读读不互斥,读写,写写,写读互斥。

永远别让世俗淹没生活的热情和浪漫 834

卷积神经网络中的边缘检测:从Canny算法到深度学习(PyTorch实现)

本文系统梳理了边缘检测从经典Canny算法到深度学习方法的演进,重点解析了Canny算法的数学原理及其对卷积神经网络设计的深远影响。通过PyTorch实战,详细演示了如何构建并训练一个基于HED架构的深度学习边缘检测模型,并探讨了模型优化、评估及与传统方法融合的高级技巧。

jenkins8butler的博客 151

ReentrantReadWriteLock详解

ReentrantReadWriteLock 只是读写锁思想的一个具体Java实现。重要的是理解这种思想。掌握这些思想可以帮助我们在不同编程语言或框架中应用类似的锁机制。《图解Java多线程设计模式》《Java并发编程的艺术》

落日的博客 1182

system_server_lowmem真的是内存不足吗?

Android系统中的system_server_lowmem真的表示系统的内存遇到瓶颈了吗?以及遇到这类日志该如何分析系统的内存,本文会回答这个问题。

jidapenglin28的专栏 513

配置 SAP ABAP Secure Storage

系统在进行数据加密时,会将数据与当前 Key ID 进行关联,也就是说,如果此时使用的是 Key ID 4713,则该条数据会在 Encrypt.Data 字段存储一个由密钥 4713 加密后的密文。Key ID 代表了不同的密钥标识,Current Key 表示系统当前正在使用的主密钥,Value 则是对应密钥的存储值,Management Data 通常包括一些管理性或辅助性的信息,用于描述密钥的状态、过期时间或者其他属性。对于每一条被加密的业务数据,系统会记录它使用了哪一条 Key ID。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 565

ReentrantReadWriteLock 简介

ReentrantReadWriteLock 简介

红血哥 1498

毕设帮扶:从0到1搭建基于YOLOv5的打架斗殴检测系统——助你搞定深度学习毕设

做完这个毕设,你已经掌握了从数据准备到模型部署、界面开发的全流程。把模型部署到边缘设备(如安防摄像头的嵌入式系统)上,实现本地化实时检测与报警;加入行为严重程度分析(如轻微推搡 vs 严重斗殴)、涉事人员数量统计,让系统从“检测”升级为“智能安防决策”;尝试融合声音、红外等多模态数据,打造全方位的暴力事件监测系统。按照这个教程一步步做,你的毕设肯定能做得很扎实。要是过程中遇到啥问题,别担心,慢慢梳理或者咱们一起聊聊,一定能把这个毕设做成你满意的作品。

LXY_LOVE36的博客 121

ReentrantReadWriteLock 源码debug详解

ReentrantReadWriteLock 源码debug详解

weixin_46569562的博客 1422

Himall商城ExpressDaDaHelper 取消订单(线上环境)

Himall商城ExpressDaDaHelper 取消订单(线上环境)

GZM888888的博客 399

self‑improving‑agent 安装与配置指南(OpenClaw)

长期记忆错误反思用户偏好学习持续进化AI → 反思 → 学习 → 改进。

tiantiantbtb的博客 1340

ReentrantReadWriteLock的使用

ReentrantReadWriteLock是AQS的一种实现,分为读锁(共享锁)和写锁(排他锁,独占锁),他可以做到在只有读的时候不阻塞,有写的时候阻塞,具体使用如下: package test.lock.read.write.lock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @author: xby * @Date: 2020/5/22 13:36 */ public class TestRea.

xby7437的博客 1万+

【数据集】1970-2023年全球温室气体排放 GHG 数据集 EDGAR

1970-2023年全球温室气体排放 GHG 数据集 EDGAR

WW、forever的博客 1173

说说ReentrantReadWriteLock

<br />1) 背景<br />     jni的使用场景,多线程读,单线程写,写的时候会更新java对象,当老的java对象无须再使用的时候必须释放jni所占用本地方法区的内存,这个区域的内存不在java heap范畴,因此也无法被垃圾回收掉,需要显式的释放。<br /><br /> 但问题在于什么释放?<br /><br /> 有人会说使用finalize,但finalize过于依赖jvm的回收的时机,这使得什么时候能真正释放显得不太好预测。<br /><br /> 或者使用synchroni

pwlazy的专栏 9629

Monorepo与pnpm:前端项目管理的完美搭档

pnpm又称 performant npm,翻译过来就是高性能的npm。Monorepo是一种项目开发与管理的策略模式,它代表"单一代码仓库"(Monolithic Repository)。在Monorepo模式中,所有相关的项目和组件都被存储在一个统一的代码仓库中,而不是分散在多个独立的代码仓库中,这些项目之间还可能会有依赖关系。

m0_68271787的博客 2562

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

ReentrantReadWriteLock的源码详细剖析

ReentrantReadWriteLock,即读写锁,是JUC中一个极其重要的类,实现了读写的分离,极大的提高了并发量,源码延续了以往一贯的鬼斧神工,让人受益匪浅建议先读《ReentrantLock的详细源码剖析》 以及 《AQS条件队列源码详细剖析》后再行阅读!注:本文适合想要深究读写锁的实现和原理,ReentrantLock的机制的人参考,文章较长读写锁到这里就结束了,总的来说,单单根据一个读写锁并不能展现共享锁的全部。共享锁最重要的资源与限制在读写锁中都没有出现。

qq_30004513的博客 1102

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

ReentrantReadWriteLock学习

Java之ReentrantReadWriteLock类

weixin_44877172的博客 989

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

ReentrantReadWriteLock原理详解

当读操作远远高于写操作时,这时候使用读写锁让读——读可以并发,提高性能。 类似于数据库中的select.,from..lockinsharemode 提供一个数据容器类内部分别使用读锁保护数据的read()方法,写锁保护数据的write()方法 简单来说,就是读锁可以共享,但是写锁必须独占 一、ReentrantReadWriteLock JUC为我们提供了一个可用于读和写的读写锁。 简单看下使用方法: ReentrantReadWriteLo...

m0_53127809的博客 3469

ReentrantReadWriteLock读写锁

B线程获取了2次读锁资源。**写锁:** 写锁的实现就是每一次获取写锁时,会对state的低16位+1,再次获取,再次+1。正常的操作,是new ReentrantReadWriteLock的对象,但是你具体的业务操作是需要读锁,还是写锁,你需要单独的获取到,然后针对性的加锁。这里可以让最后一个获取读锁的线程在重入时,省略掉去ThreadLocal中get计数器的操作,但是之前的set存储操作,不能省略。在读写锁中,持有读锁的同时,去获取写锁,这种行为被称为 **锁升级** ,这个行为是不允许的。

weixin_46300935的博客 1188

ReentrantReadWriteLock类

同时其还实现了Serializable接口,表示可以进行序列化,在源代码中可以看到ReentrantReadWriteLock实现了自己的序列化逻辑。在某些情况下,使用ReentrantReadWriteLock可能更高效,因为它允许多个线程同时读取共享资源,从而减少了锁竞争的可能性。ReentrantReadWriteLock是一个读写锁,它允许多个读操作同时进行,但在写操作时会阻止其他所有读和写操作。ReadThread和WriteThread是自定义的线程类,它们分别代表读线程和写线程。

超级博客 977

ReentrantReadWriteLock使用场景思考及验证

ReentrantReadWriteLock使用场景思考CachedDataRWDictionary思考和验证 最近一直在琢磨ReentrantReadWriteLock的使用场景是什么,后来突然就想明白了,其实就是读读并发、读写互斥、写写互斥而已。如果一个对象并发读的场景大于并发写的场景,那就可以使用ReentrantReadWriteLock来达到保证线程安全的前提下提高并发效率。首先,我们先了解一下Doug Lea为我们准备的两个demo。 CachedData 一个缓存对象的使用案例,缓存对象在使用

wx的博客 3061

ReentrantLock、ReentrantReadWriteLock、StampedLock

ReentrantLock、ReentrantReadWriteLock、StampedLock

qq_44300280的博客 1445

ReentrantReadWriteLock读写锁的介绍

本篇文章将分享读写锁相关的知识点,适合在学Java的小白,也适合复习中,面试中的大佬

小威的博客 2万+
上一篇: ReentrantLock入门级
下一篇: Redis入门篇(1)-类型技能
yi博说
博客等级 码龄11年 20粉丝 30原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值