Java并发(十二):读写锁——读锁

多线程之读写锁原理 今天主要通过多读单写的例子来说下读写锁的原理 概念 多读单写,简单说,就是对资源的访问分为两种状态,一种是读操作,另一种是写操作。由应用程序提示锁应该做哪种操作。当为读模式时,所有的写动作被悬挂,而读请求被允许通过,而写动作时,所有操作被悬挂。并且,读写切换时,有足够的状态等待,直到真正安全时,才会切换动作。 如下图所示: 业务场景举例 比如现在有 A、B、C、D、E、F、G 6个线程,其中A、B、C、G 4个线程之行读请求,E、F 2个线程之行写请求,如何保证读写安全? 分析: .. 阅读详情

回顾

前面我们已经看了写锁是怎样进行获取和释放的,下面就来看看读锁的获取和释放

读锁

写锁是一个排他锁,而读锁却是一个共享锁,而且还支持可重入,并且能被多个线程同时获取

读锁的获取

在这里插入图片描述
在这里插入图片描述
可以看到,其底层的实现也是AQS,只不过是AQS的共享模式,并且执行的是AQS的acquireShared方法

源码如下

    public final void acquireShared(int arg) {
        //执行tryAcquireShared方法尝试进行获取共享锁
        if (tryAcquireShared(arg) < 0)
            //获取共享锁失败,进行CAS自旋(写锁被获取了)
            doAcquireShared(arg);
    }

可以看到获取共享锁的主要方法为tryAcquireShared

tryAcquireShared

源码如下

        protected final int tryAcquireShared(int unused) {
            /*
             * Walkthrough:
             * 1. If write lock held by another thread, fail.
             * 2. Otherwise, this thread is eligible for
             *    lock wrt state, so ask if it should block
             *    because of queue policy. If not, try
             *    to grant by CASing state and updating count.
             *    Note that step does not check for reentrant
             *    acquires, which is postponed to full version
             *    to avoid having to check hold count in
             *    the more typical non-reentrant case.
             * 3. If step 2 fails either because thread
             *    apparently not eligible or CAS fails or count
             *    saturated, chain to version with full retry loop.
             */
            //依然从注释上开始看起,注释同样提到了三点
            //1.如果其他线程拥有了写锁,获取共享锁失败
            //2.如果没有写锁,就代表可以获取共享锁,不过要现判断是否应该进行阻塞(由于队列策略)
            //	并且通过CAS的方法更新同步状态,更新成功就代表获取锁成功
            //3.如果获取共享锁失败,代表CAS失败或者共享锁数量满了
            
            //获取当前线程
            Thread current = Thread.currentThread();
            //获取当前同步状态量
            int c = getState();
            //获取低位,也就是获取写锁的同步状态量
            //判断写锁的同步状态量是否为0,即有没有线程去获取写锁
            //如果写锁被人获取了,再判断写锁的拥有者是不是当前线程
            //因为规定拥有写锁的线程可以去拥有读锁
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                //如果写锁被获取了,且写锁不是被自己获取
                //获取共享锁失败,返回-1
                return -1;
            //获取共享锁的同步状态量
            int r = sharedCount(c);
            //判断获取共享锁需要不需要进行阻塞
            //判断共享锁的同步状态量是不是达到了最大值
            //判断CAS改变共享锁的同步状态量是否成功
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                //如果共享锁的同步状态量为0,证明是第一个去获取共享锁
                //注意此时这里会有并发问题
                //假如第一个线程在CAS这里停止了,那么后面线程获取的同步状态量依然为0
                if (r == 0) {
                    //将第一个获取共享锁的线程设置为当前线程
                    //这里是有并发问题的
                    firstReader = current;
                    //记录第一个线程获取共享锁的次数
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                    //如果第一个获取共享锁的线程,又去获取共享锁
                    //次数+1,代表重入了
                    firstReaderHoldCount++;
                } else {
                    //如果是其他线程进来获取共享锁
                    //定义一个HoldCounter缓存
                    HoldCounter rh = cachedHoldCounter;
                    //HoldCounter缓存存储在ThreadLocalMap中
                    if (rh == null || rh.tid != getThreadId(current))
                        //如果缓存为空,代表第一次其他线程进来
                        //如果缓存不为空,但此时的缓存不属于当前线程(线程ID不对应)
                        //从ThreadLocal的ThreadLocalMap中去取当前线程的缓存
                        cachedHoldCounter = rh = readHolds.get();
                    else if (rh.count == 0)
                        //如果缓存中的count为0
                        //将缓存添加进容器中
                        readHolds.set(rh);
                    //通过holdCounter记录其他线程获取的共享锁次数
                    rh.count++;
                }
                //返回1,代表加锁成功
                return 1;
            }
            //如果CAS失败或其他原因导致失败
            //调用fullTryAcquireShared方法
            //其实里面底层是
            return fullTryAcquireShared(current);
        }

从代码上可以看到,共享锁其实还挺复杂

  • 判断写锁有没有被线程获取

    • 如果写锁被获取了,再判断写锁是不是被当前线程获取,如果是当前线程获取,仍然可以继续去获取读锁
    • 如果写锁被获取了,且并不是当前线程获取的,直接reutnr -1,代表获取读锁失败
  • 判断是否需要进行阻塞

  • 判断读锁的同步状态量是否达到最大值

  • 判断CAS更换读锁的同步状态量是否成功

  • 只有满足上面三个判断,才代表获取锁成功

  • 获取锁成功之后就要进行记录重入次数了,虽然我们更改了读锁的同步状态量,但读锁的同步状态量是一个整体的,而读锁是共享的,即每个线程都有,所以需要每个线程去记录读锁的重入次数

    • 对于第一个获取共享锁的线程,进行记录,并且记录其重入次数
    • 对于其他线程,是使用ThreadLocal来进行存储的,具体的底层容器为ThreadLocalMap
  • 如果获取锁失败,会调用fullTryAcquireShared方法,这个方法的本质上是CAS进行tryAcquiredShared。。。

前面的步骤都还好理解,重点在于读锁怎么去存储各个线程的重入次数的

记录各个线程的重入次数

对于被firstReader记录的线程还好说,通过firstReaderHoldCount进行记录即可

但对于其他线程,则是利用ThreadLocalMap来进行存储的

HoldCounter

每个线程对应的缓存其实就是一个HoldCounter

在这里插入图片描述
可以看到,这个类里面有两个属性

  • count:记录重入次数
  • tid:记录线程ID

从前面可以看到,当前缓存为空,或者该缓存并不属于当前线程(线程ID不对应),那么就会走readHolds的get方法

从判断条件可以看到,这个get方法应该会做两种事情

  • 缓存为空,证明没创建,需要进行初始化
  • 缓存不属于当前线程,需要进行切换线程

在这里插入图片描述
在这里插入图片描述
从源码中可以看到,readHolds本质上是一个ThreadLocalHoldCounter对象,并且继承了ThreadLocal,并且泛型是HoldCounter,所以ThreadLocalHoldCounter拥有ThreadLocal的功能,而ThreadLocal有存储功能,可以将元素存储在ThreadLocalMap中

下面就来看看get方法是做什么的

readHolds.get

在这里插入图片描述
可以看到,这个方法来自ThreadLocal源码如下

    public T get() {
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        //如果ThreadLocalMap不为空
        if (map != null) {
            //根据当前ThreadLocal去获取存储的键值对
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                //如果键值对不为空,返回value
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        //如果ThreadLocalMap为空,需要进行初始化
        return setInitialValue();
    }

从这里大概就可以看到大致的模型了

当前线程的ThreadLocalMap中存储了以ThreadLocalHoldCounter的父类ThreadLocal为键的,HoldCounter为值的键值对,键是每个线程都能拿到的,但这里能进行区分是因为每个线程都存在了自己的ThreadLocalMap中,这样就进行区分了

在这里插入图片描述
在这里插入图片描述
可以看到,其底层其实是一个Entry数组

setInitialValue方法

这个方法其实就是当ThreadLocalMap没有创建的时候,需要进行初始化的时候调用

    */
    private T setInitialValue() {
        //获取默认值
        //默认值为null
        T value = initialValue();
        //获取当前线程
        Thread t = Thread.currentThread();
        //再次判断有没有创建
        ThreadLocalMap map = getMap(t);
        if (map != null)
            //如果已经创建,直接set方法改变里面的entry属性
            map.set(this, value);
        else
            //如果没创建,就进行创建
            createMap(t, value);
        return value;
    }

在这里插入图片描述

set方法

当count属性为0时,会调用set方法,这个方法其实是用来修改值的或者为空时进行初始化的,没太看懂这个是干嘛的。。是为0时还没有进入缓存吗?

在这里插入图片描述
最后初始化缓存和容器了之后,就进行对缓存的count属性进行自增1了,这样就对应记录了每个线程的重入次数了

假如第一次获取失败

第一次获取失败是会执行fullTryAcquireShared方法的,这个方法名也挺有讲究,翻译为完全尝试,前面先尝试,尝试失败就进行完全尝试

源码如下

        final int fullTryAcquireShared(Thread current) {
            /*
             * This code is in part redundant with that in
             * tryAcquireShared but is simpler overall by not
             * complicating tryAcquireShared with interactions between
             * retries and lazily reading hold counts.
             */
            HoldCounter rh = null;
            //死循环
            //已经在开始自旋获取锁了
            for (;;) {
                //获取锁的状态量
                int c = getState();
                //获取写锁的状态量
                if (exclusiveCount(c) != 0) {
                    //并且判断,如果有人获取了写锁,并且该写锁的拥有者不是自己,返回-1
                	//交由AQS去实现共享锁了
                    if (getExclusiveOwnerThread() != current)
                        return -1;
                    // else we hold the exclusive lock; blocking here
                    // would cause deadlock.
                }
                //如果没人获取写锁,或者写锁的拥有者是自己
                //判断获取读锁需不需要进行阻塞
                //这个方法对于公平锁和非公平锁有不同实现
                //为什么要判断要阻塞了,这就涉及到公平与非公平的性质
                //公平的话,肯定是要进行排队的,非公平的话不需要进行排队
                else if (readerShouldBlock()) {
                    // Make sure we're not acquiring read lock reentrantly
                    //如果进来需要进行阻塞,也就意味着需要进行排队,即前面已经有线程等待了
                    //这里是保证当前线程没有以可重入的方式获取锁的
                    //因为如果是以可重入的方式去获取锁,那就没有什么插不插队的概念了
                    //就比如买票看电影一样,你已经排队买了电影票了,然后有事离开了一下,那么下一次进来还需要排队买票吗?
                    //判断firstReader是不是当前线程,如果是就不用清除了
                    //因为第一个获取读锁的线程由ReentrantReadWriteLock维护
                    if (firstReader == current) {
                        //并且如果是第一个线程获取的,那就代表发生可重入了,没有插队概念
                        //不做任何处理,继续获取锁
                        // assert firstReaderHoldCount > 0;
                    } else {
                        //如果第一个获取读锁的线程不是自己,那就代表有其他线程已经拿到锁了
                        //下面就需要进行判断,当前线程获取读锁需不需要进行排队
                        if (rh == null) {
                            //获取缓存,即前一次获取锁的线程
                            rh = cachedHoldCounter;
                            //如果为空或者缓存的线程ID不为自己
                            //代表了
                            if (rh == null || rh.tid != getThreadId(current)) {
                                //获取自己ThreadLocal存储的共享锁状态量
                                rh = readHolds.get();
                                //如果为0.清空掉
                                if (rh.count == 0)
                                    //清空掉当前线程的ThreadLocal的读锁状态量
                                    readHolds.remove();
                            }
                        }
                        //为什么此时rh.count仍然为0就return????
                        //因为进入到这里代表了线程需要进行阻塞,而阻塞是交由AQS决定的
                        //如果是第一次获取锁,就代表没有发生可重入,那就需要进行排队了
                        //返回-1,让AQS去实现阻塞排队
                        if (rh.count == 0)
                            return -1;
                    }
                }
                //判断现在读锁的状态量是不是已经为最大值了
                if (sharedCount(c) == MAX_COUNT)
                    //如果为最大值,代表读锁的数量已经满了,不能获取了,抛出异常
                    throw new Error("Maximum lock count exceeded");
                //如果没满最大值,那就进行CAS替换状态量
                if (compareAndSetState(c, c + SHARED_UNIT)) {
                    //替换成功就进行获取锁
                    //上面已经分析过,这里不再赘述
                    if (sharedCount(c) == 0) {
                        firstReader = current;
                        firstReaderHoldCount = 1;
                    } else if (firstReader == current) {
                        firstReaderHoldCount++;
                    } else {
                        if (rh == null)
                            rh = cachedHoldCounter;
                        if (rh == null || rh.tid != getThreadId(current))
                            rh = readHolds.get();
                        else if (rh.count == 0)
                            readHolds.set(rh);
                        rh.count++;
                        cachedHoldCounter = rh; // cache for release
                    }
                    //获取锁成功就进行return
                    return 1;
                }
                //获取锁失败,就进行下一轮循环
            }
        }

可以看到,这个方法其实是在CAS获取读锁的基础上,添加了阻塞功能,也就是说完整的尝试获取锁,不仅是判断写锁的状态和读锁的状态,还需要去考虑是否要阻塞

而考虑这个阻塞问题,其实就是关于公平锁和非公平锁的判断了,这里要注意一点的就是可重入锁是没有排队这个概念的!!!!

AI大模型开发——transformer模型超全完整版(0基础可懂) 为了更深入的了解transformer模型,我们首先从transformer的诞生聊起吧。transformer在2017年由Vaswani等人首次提出,它摒弃了之前流行的循环神经网络和卷积神经网络的架构,引入了自注意力机制( Self-Attention),显著提高了处理序列数据的能力和效率。transformer模型的这种设计使其在处理长距离依赖问题时, 相比于传统的循环神经网络和长短期记忆网络(LSTM)表现得更加出色,至此, 开启了NLP技术的新篇章。 阅读详情

相关推荐

SSD性能测试进阶:用fio的io_uring引擎榨干你的NVMe硬盘

本文深入探讨了如何利用fio的io_uring引擎对NVMe SSD进行深度性能测试。通过对比传统libaio引擎,详解了io_uring低开销、高并发的优势。文章提供了从测试矩阵设计、队列深度分析到写入放大观察和尾延迟可视化的完整进阶方法,并附有实战案例与脚本,旨在帮助用户科学压榨NVMe硬盘的极限性能,获取更真实的评估数据。

weixin_29247219的博客 326

ReentrantReadWriteLock分析

ReentrantReadWriteLock分析 依然使用state变量表示锁的获取状态,因为读写锁涉及读锁和写锁,因此用state变量的高16位和低16位作为区分,分别代表读锁的获取次数,以及写锁的重入次数。 1、写锁加锁过程 protected final boolean tryAcquire(int acquires) { // 当前获取锁的线程 Thread current = Thread.currentThread(); //

博客 216

嵌入式硬件实战:RC、LC与RL滤波电路的设计与选型指南

本文深入探讨了嵌入式系统中RC、LC与RL滤波电路的设计与选型策略。通过生活化比喻和实战案例,详细解析了三种滤波电路的工作原理、应用场景及工程注意事项,帮助工程师根据电流大小、频率特征和成本预算选择最优方案,提升嵌入式硬件设计的稳定性和抗干扰能力。

kiwi8的博客 284

java read write lock_理解ReentrantReadWriteLock可重入读-写锁(转)

abstractbooleanreaderShouldBlock();abstractbooleanwriterShouldBlock();写锁的tryXXX获取和释放1.写锁释放时,由于没有其他线程获得临界区,它的tryRelease()方法只需要设置状态的值,通过exclusiveCount计算写锁的计数器,如果为0表示释放了写锁,就把exclusiveOwnerThread设置为null.2...

weixin_31485835的博客 205

vue高德地图POI关键字搜索定位

最近在项目中遇到了关于地图的问题,首先为了满足需求从而使用的是JSAPI为2.0版本的。需求是根据关键字搜索定位到该位置。值得注意的是在使用 AMap.Event.addListener时 Event一定要大写的E,不然页面会报错。在地图上定义一个输入框,根据输入的位置进行提示,然后点击后跳转到对应的位置并打点标记。JSAPI主要使用这两个。

YL 2136

实战对比:VINS和ORBSLAM3在无人机避障中的表现(含实测数据)

本文通过实战测试,深度对比了VINS和ORBSLAM3两种主流视觉SLAM算法在无人机避障中的表现。测试聚焦于光照变化、动态遮挡和快速运动三大挑战,提供了详细的实测数据与性能分析。结果表明,VINS在计算效率和实时性上占优,而ORBSLAM3在定位精度和地图管理方面更稳健,为不同应用场景下的工程选型提供了清晰依据。

醉三国 627

ReentrantReadWriteLock原理分析

待更新

weixin_44399827的博客 250

Centos7网络配置(设置固定ip)

刚安装完系统,有的人尤其没有勾选自动网络配置的情况下,是访问不了外网的,本机也连不通虚拟机。或者现在有的是动态ip,几次之后启动ip地址就变了,会导致其他软件或程序之前连接配置要重新更改,很麻烦,下面讲解一下如何配置网络,如何设置成固定ip。

个人笔记git:https://gitee.com/tlangp/note.git 8054

【基本功】不可不说的Java“锁”事

总第303篇2018年 第95篇并发编程是Java程序员必备基本功,今天“基本功”专栏向大家推荐一篇深入解析Java锁机制的文章。Enjoy!前言Java提供了种类丰富的...

美团技术团队 4217

STM32 SYSTICK定时器及延时函数详解

STM32 SYSTICK及延时函数详解 SysTick定时器 SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。在以前,大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。例如,为多个任务许以不同数目的时间片,确保没有一个任务能霸占系统;或者把每个定时器周期的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心

w1050321758的博客 2366

进阶06:C#连接TwinCAT 3使用ADS通讯

本文介绍了使用C#通过ADS协议与倍福TwinCAT3 PLC通讯的实现方法。主要内容包括:1)在两台电脑上安装TwinCAT3开发环境并建立网络连接;2)在TwinCAT中定义变量和结构体;3)使用C# Winform开发界面,通过TwinCAT.Ads.dll实现读写功能;4)实现主动读取和通知两种数据获取方式。详细说明了连接建立、变量定义、读写操作及事件通知的实现代码。该方案实现了对PLC寄存器数据的实时监控和操作,为工业控制系统的开发提供了参考。

weixin_38637405的博客 3688

[并发进阶]——读写锁 原理

通过图解流程以及阅读源码的方式带你深入理解读写锁,让你真正理解其原理

热爱编程的机械生的救赎 1952

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

图文深入解析 JAVA 读写锁,为什么读锁套写锁会死锁,反过来却不会?

一、回顾基本的读写锁 我们知道读写锁 #java.util.concurrent.locks.ReentrantReadWriteLock 是一个 “读写互斥,写写互斥,读读共享” 的锁。 读写锁的使用非常简单,那就是: 我们只需要保证读锁和写锁来自同一个 ReentrantReadWrit...

chongshizhuo8736的博客 2631

ssm框架搭建

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

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

33.Java读写锁(认识读写锁、读写锁案例、锁降级、锁的演变)

33.Java读写锁(认识读写锁、读写锁案例、锁降级、锁的演变)

weixin_52173250的博客 1569

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Java并发编程--12-读写锁与StampedLock:高并发读场景下的性能优化利器

本文深入分析了Java并发编程中读写锁与StampedLock的应用场景与技术原理。针对读多写少的高并发场景,传统独占锁会导致性能瓶颈,而读写锁通过"读读共享、读写互斥"机制显著提升并发度。文章详细解读了ReentrantReadWriteLock的核心设计、锁降级实现及写饥饿问题,并对比了JDK8引入的StampedLock创新性"乐观读"模式。通过性能测试数据、代码示例和架构图,系统性地阐述了两种锁的适用场景、实现原理和最佳实践,为开发者在高并发系统优化中提供了关

weisian的博客 7288

并发编程:读写锁

本文主要介绍并发编程中读写锁的知识

AWSDN的博客 1145

Java并发之-读写锁ReentrantReadWriteLock

前言 之前提到的ReentrantLock是排他锁,这种锁同一时刻只允许一个线程访问,而读写锁同一时刻可以多个线程访问,但在写线程访问时,所有读线程和其他写线程都要被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读写锁,使得并发性相比一般的排他锁有很大提升。 参考文献 《Java并发编程的艺术》 正文 读写锁只需要在读操作时获取读锁,写操作获取写锁即可。当写操作被获取时,后续读写...

晏霖/胖虎的博客 4679

java写多读少用什么锁_Java 读写锁 原来这么简单

Java ReentranReadWriteLock前言一、技术介绍1.ReentranReadWriteLock是什么?二、源码分析1.ReadLock2.WriteLock三、单元测试总结作者寄语提示:下方有源代码地址,请自行拿取前言在实际项目中,比如我们有一个共享资源文件,我们程序会会同时并发的去读、写这个共享资源文件,那怎么能保证在高并发场景下安全、高效读写呢?OK,看了下文便知提示:以下...

weixin_29962351的博客 1333

33、java读锁应该插队吗?以及什么是读写锁的升降级

对于 ReentrantReadWriteLock 而言。插队策略公平策略下,只要队列里有线程已经在排队,就不允许插队。非公平策略下:如果允许读锁插队,那么由于读锁可以同时被多个线程持有,所以可能造成源源不断的后面的线程一直插队成功,导致读锁一直不能完全释放,从而导致写锁一直等待,为了防止“饥饿”,在等待队列的头结点是尝试获取写锁的线程的时候,不允许读锁插队。

zht245648124的博客 1064

Java中的锁——ReentrantReadWriteLock(读写锁)

上一篇里讲的ReentrankLock是一种排他锁,即同一时间只能有一个线程进入。而读写锁在同一时刻允许多个读线程访问,但是在写线程访问时,所有的读线程和其他线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读写锁,使得并发性比一般的排它锁有了很大提升。因为大多数应用场景都是读多于写的,因此在这样的情况下,读写锁可以提高吞吐量。下图描述了关于读写锁的三个特性:公平性、重入性和锁降级。

惟愿无事 3258

java 读锁有什么用_什么是读写锁

我们知道多个线程同时读一个资源类是没有任何问题的,所以在并发的情况下,读取共享资源应该是可以同时进行的;但是,如果一个线程想要去写共享资源,就不应该再有其他线程同时对该共享资源进行读或者是写操作了。我们想要的是:允许多个线程同时读,但只要有一个线程在写,其他线程就必须等待。读写锁就是这个原理,即读写锁在同一时刻可以允许多个多线程访问,但是在写线程访问的时候,所有的读线程和其他写线程都会被阻塞。读写...

weixin_35693055的博客 897

java并发锁ReentrantReadWriteLock读写锁源码分析

1、ReentrantReadWriterLock 基础 所谓读写锁,是对访问资源共享锁和排斥锁,一般的重入性语义为如果对资源加了写锁,其他线程无法再获得写锁与读锁,但是持有写锁的线程,可以对资源加读锁(锁降级);如果一个线程对资源加了读锁,其他线程可以继续加读锁。 java.util.concurrent.locks中关于多写锁的接口:ReadWriteLock。 public inte...

中间件兴趣圈 2万+

java并发之ReentrantReadWriteLock的读锁

ReentrantReadWriteLock是juc包下的一个锁工具类,可以同时获取读锁和写锁,读锁底层为共享锁,写锁底层为独占锁.读锁可以重复获取,写锁只可以有一个线程获取,并且读写锁互斥.读锁不可以升级为写锁.而写锁可以降级为读锁.是为了保证多线程情况下数据的可见性.

不愿言弃-念何的博客 674

java线程之读写锁

一、什么是读写锁 读写锁是JDK1.5提供的一个工具锁,适用于读多写少的场景,将读写分离,从而提高并发性。 二、读写锁的特点 读锁是共享锁,写锁是排他锁,读锁和写锁不能同时存在; 读锁不能升级为写锁; 写锁可以降级为读锁; 三、代码演示 import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lo

一起coding,一起嗨。 7525

并发锁之二:ReentrantReadWriteLock读写锁

一、简介   读写锁是一种特殊的自旋锁,它把对共享资源对访问者划分成了读者和写者,读者只对共享资源进行访问,写者则是对共享资源进行写操作。读写锁在ReentrantLock上进行了拓展使得该锁更适合读操作远远大于写操作对场景。一个读写锁同时只能存在一个写锁但是可以存在多个读锁,但不能同时存在写锁和读锁。   如果读写锁当前没有读者,也没有写者,那么写者可以立刻获的读写锁,否则必须自旋,直到没有...

GarfieldEr007的专栏 754
上一篇: Spring(十二):bean的加载——创建bean
下一篇: Java并发(十一):读写锁——写锁
GDUT_Ember
博客等级 码龄6年 875粉丝 190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值