网上很多大神写了关于AQS和读写锁的源码分析,看了收益良多。但是发现很少有基于读锁和写锁被持有的场景结合源码进行分析的,
所以尝试记录自己的分析结果,如果有人有暇发现了错误,请不吝赐教。
1: 读锁请求和释放
readLock.lock()
多个读请求锁,主要是调用ReentrantReadWriteLock的tryAcquireShared()方法
下面仔细分析下这个方法:
tryAcquireShared()方法:
①:判断是当前否存在独占锁,如果存在独占锁,那么返回-1,进入doAcquireShared方法()
②:判断readshouldBlock方法,这个方法判断是否需要当前读阻塞。在公平锁的前提下是看SYN队列是否存在先驱节点;如果是非公平模式则看队列中
第一个是否为写。(多个读场景不可能阻塞因为 没有写 读是不会进入阻塞队列)
③:如果读锁持有的数量小于MAX_COUNT(2^32-1=65535)则,通过CAS操作来比较。如果比较成功则返回成功;如果失败则进入步骤④FullAcqurireShared方法
④:FullAcqurireShared() 其实就是重复1,2,3步骤,知道获取到state或者返回-1.这里需要注意的是这里用例ThreadLocal对象readHolds来存储当前线程锁重入的次数。
如果上述方法返回-1,基本上归结为公平模式下SYN的队列中有先驱,或者非公平模式下有阻塞的写在队列头中(这种情况一般是被之前读锁阻塞的写),则进入doAcquired方法。
doAcquireShared()方法:
①:把当前线程包装成共享类型的Node放入SYN队列中
②:如果当前节点前驱为头结点(既当前节点是队列中的第一个阻塞节点),则再一次调用tryAcquireShared()方法
(需要说明的,这边这样做的原因可能是在这个读锁请求过程中,已阻塞在队列中的的读或者写已经被释放。)
如果tryAcquiredShared()成功了,则尝试释放队列中的存在后继节点且前继waitStatus小于0 则调用doReleaseShared()方法
③:如果第二步失败,则用LockSupport.park()挂起当前节点,进入阻塞状态
doReleaseShared()方法:
这里需要注意的是前一个节点的waitStatus代表后一个节点的状态,如果是singnal状态 则代表后一个节点所在线程已经被挂起。
所以doReleaseShared()方法中 unpark的是singanal状态的节点。队列中下一个节点的unpark()是由前一个节点先前阻塞的doAcquireShared()
方法锁唤醒。
readLock.unlock()
tryReleaseShared()方法:
①:更具ThreadLocalmap中的 readHolds对象,并把数量-1
②:CAS把高位读state-1.如果修改后的state为0 则返回true;否则返回false
③:步骤②返回true 代表需要进行doRelaeaseShared方法。这里这样做的原因是 当前锁不被 任何读锁或者写锁持有,则doReleaseShared()。因为如果队列中有写请求,写锁需要等刀所有读释放掉才能获得,后于这个写锁来的读锁,肯定被加入到了队列中;如果队列中没有写请求,那么队列中肯定也不会有阻塞(之前和这个读一起阻塞的读线程已经被unpark掉) 因为读是共享的
2:写锁请求和释放
writeLock .lock()
tryAcquire()方法:
tryAcquire()方法 RRWL重写了这个方法
①: 如果state不为0 且写锁数为0,则代表当前有读锁被持有,当前写阻塞;如果state不为0,写锁数w不为0且独占线程=本身,则当前lock请求为锁的重入
②:如果state为0,则表示当前没有写锁或者读锁被持有。判断writerShouldBlock()当前线程是否要阻塞:如果公平模式下,判断是否有前驱节点,非公平模式
下,直接返回false
③:步骤②返回true 则当前线程获得锁;返回false,则用当前线程封装一个独占节点Node. EXCLUSIVE,并加入AQS的队列,进入步骤acquireQueued()
acquireQueued()方法:
①: 以当前线程构造独占节点并加入到AQS的队列中
②:如果当前的前驱节点是head节点,则再次调用tryAcquired方法,这边再次判断的原因是可能的读锁或者写锁已经释放,如果tryAcquired失败
则挂起当前线程
writeLock .unlock()
①:tryRelease()方法:
unLock必须是取得锁的线程,因为是独占的 写锁只有一个 所以判断独占线程是否一致 不一致则抛出异常;一致就把state-1,如果写锁被持有数为0
则把独占线程置成null。
②:unparkSuccessor后继节点
1)判断head是否为空,既判断AQS队列中是否有元素,如果有head节点不为null且waitStatus不为0,则进入步骤2)
2)进入unparkSuccessor()方法,这个方法意义和名字一样 释放后续的阻塞线程
2.1)如果当前节点状态小于0则给他置0,这里当前节点状态代表后继节点线程的状态
2.2)如果后继节点是null或者waitStatus>0(既为CANCELLED取消状态,则跳过),否则unpark()线程
这种情况,可能是当前写锁被持有,后续来了N个读锁的请求或者写锁请求,这几个锁请求显然被park()变成阻塞状态,放在了队列中。
unLock的unparkSuccessor,唤醒了队列中的head,依次唤醒队列的后续节点,这里需要注意的是,唤醒的顺序 不一定是获得锁的顺序!
相关推荐
此外,ReentrantReadWriteLock还提供了一些高级特性,如公平锁和非公平锁的选择,以及读写锁的升级和降级操作,使得在某些场景下能够实现更灵活的并发控制。 总之,ReentrantReadWriteLock是Java并发编程中非常重要...
《JUC并发编程与源码分析视频课》是一门深入探讨Java并发编程的课程,主要聚焦于Java Util Concurrency(JUC)库的使用和源码解析。JUC是Java平台提供的一组高级并发工具包,它极大地简化了多线程编程,并提供了更...
读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁...
"java并发源码分析之实战编程"这个主题深入探讨了Java平台上的并发处理机制,旨在帮助开发者理解并有效地利用这些机制来提高程序性能和可扩展性。在这个专题中,我们将围绕Java并发库、线程管理、锁机制、并发容器...
8. Lock接口 (ReentrantLock 可重入锁) 特性 ReentantLock 继承接口 Lock 并实现了接口中定义的方法, 它是一种可重入锁, 除了能完成 synchronized 所能完成的所有工作外,还提供了诸如可响应中断锁、可轮询锁...
其次,ReentrantReadWriteLock 可以提供公平锁和非公平锁两种锁类型,满足不同的应用场景。最后,ReentrantReadWriteLock 的实现方式简单、灵活,易于维护和扩展。 ReadWriteLock 接口及其实现 ...
根据提供的文件信息,本文将详细解析读写锁`ReentrantReadWriteLock`以及`StampLock`在Java并发编程中的应用场景及其实现原理。 ### 一、读写锁介绍 #### 1.1 读写锁的基本概念 读写锁是一种特殊的锁机制,它可以...
Java的ReentrantReadWriteLock是Java并发包`java.util.concurrent.locks`中的一个重要工具,它提供了一种更细粒度的锁定机制,相比普通的独占锁(如ReentrantLock)在某些场景下能显著提高程序的并发性能。...
Java并发包源码分析(JDK1.8):囊括了java.util.concurrent包中大部分类的源码分析,其中涉及automic包,locks包(AbstractQueuedSynchronizer、ReentrantLock、ReentrantReadWriteLock、LockSupport等),queue...
在学习Java过程中,自己收集了很多的Java的学习资料,分享给大家,有需要的欢迎下载,希望对大家有用,一起学习,一起进步。
ReentrantReadWriteLock 读写锁除了保证写操作对读操作可见性以及并发行提升外,简化了读写交互场景开发
6.5 深入理解 AQS之 ReentrantReadWritelock 实战副本.mp4
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); Map, String> map = new HashMap(); public void put(String key, String value) { lock.writeLock().lock(); try { map.put(key, value); }...
在Java多线程并发编程中,ReentrantReadWriteLock(可重入读写锁)是一个重要的同步工具,它属于Java并发包(java.util.concurrent.locks)中的一个类。这个锁提供了比标准的synchronized关键字更细粒度的控制,允许...
6.5 深入理解 AQS之 ReentrantReadWritelock 实战副本副本.mp4
04-Java并发线程池底层原理详解与源码分析-monkey 05-并发编程之深入理解Java线程-fox 06-并发编程之CAS&Atomic原子操作详解-fox 07-并发锁机制之深入理解synchronized(一)-fox 08-并发锁机制之深入理解...
3. **数据操作**: 分析读写 ZooKeeper 节点数据的源码,了解 zkclient 如何封装和优化这些操作。 4. **并发控制**: zkclient 在多线程环境下的同步和并发控制策略,如使用 ReentrantReadWriteLock 和 Semaphore。 ...
ReadWriteLock的使用,实际上由于ReadWriteLock是一个接口,所以实际使用的是ReentrantReadWriteLock子类。同时ReadWriteLock的使用其实也是比较简单的,就是读写的锁的使用以及注意事项而已。
#### 三、ReentrantReadWriteLock源码分析 - **特点**:`ReentrantReadWriteLock`支持读写分离,允许多个读锁并发持有但只有一个写锁可以被持有。这可以显著提高读多写少场景下的并发性能。 - **关键特性**: - **...