简介

ReentrantReadWriteLock,读写锁。维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的。

与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程)。当访问读写比恰当的共享数据时,使用读-写锁所允许的并发性将带来更大的性能提高。

源码分析

ReentrantReadWriteLock的实现方式是在内部定义了一个实现AbstractQueuedSynchronizer(详见:JUC 源码分析 - AbstractQueuedSynchronizer(AQS))的内部类Sync,Sync同时实现了AbstractQueuedSynchronizer中独占模式的获取和释放方法tryAcquire和tryRelease,和共享模式的获取和释放方法tryAcquireShared和tryReleaseShared,写锁WriteLock使用独占模式的方法控制锁状态,读锁ReadLock使用共享模式的方法控制锁状态,在WriteLock和ReadLock中使用同一个AQS的子类Sync,用AQS的status代表读写锁的状态计数,单个int值,通过位运算区分高低位,低16位代表写状态,高16位代表读状态。支持公平非公平实现,支持中断,支持重入,支持锁降级。

当并发读写时:

  1. 当有线程获取了独占锁,那么后续所有其他线程的独占和共享锁请求会加入同步队列等待,后续当前线程的独占和共享锁可以再次获取;
  2. 当有线程获取了共享锁,那么后续所有线程的独占锁请求会加入同步队列,后续所有线程的共享锁请求可以继续获取锁;
  3. 当独占锁完全释放时,会唤醒后继节点,当唤醒的是共享节点时,会传播向后唤醒后继的共享节点;
  4. 当共享锁完全释放时,且当前没有持有独占锁,会唤醒后继节点,当唤醒的是共享节点时,会传播向后唤醒后继的共享节点;
  5. 当当前线程已经获取独占锁,那么当前线程可以继续获取共享锁,当独占锁退出时,锁降级为共享锁;
  6. 一个线程可以同时进入多次共享锁或独占锁;

Sync

    abstract static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 6317671515068378041L;static final int SHARED_SHIFT   = 16;//32位分高16和低16位static final int SHARED_UNIT    = (1 << SHARED_SHIFT);//0000000000000001|0000000000000000static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;//0000000000000000|1111111111111111static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;//0000000000000000|1111111111111111static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }//获取共享锁计数,高16位static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }//获取独占锁计数,低16位static final class HoldCounter {//每个线程持有的读锁计数。写锁独占所以写锁的低16位就代表独占线程重入计数,读锁共享,所以读锁的高16代表所有线程所有重入次数的计数,HoldCounter用ThreadLocal保存每个线程自己的重入计数。int count = 0;final long tid = Thread.currentThread().getId();}static final class ThreadLocalHoldCounterextends ThreadLocal<HoldCounter> {public HoldCounter initialValue() {//初始化return new HoldCounter();}}private transient ThreadLocalHoldCounter readHolds;//保存每个线程持有的读锁计数,不参与序列化。private transient HoldCounter cachedHoldCounter;//缓存最新获取共享锁的线程的HoldCounterprivate transient Thread firstReader = null;//缓存第一个获取共享锁的线程private transient int firstReaderHoldCount;//缓存第一个获取共享锁的线程的重入计数Sync() {readHolds = new ThreadLocalHoldCounter();setState(getState()); //用volatile的读写保证readHolds的可见性,保证readHolds对所有线程可见。}abstract boolean readerShouldBlock();//共享锁获取是否需要阻塞。控制共享模式的获取锁操作是否公平,子类实现。公平实现会看当前同步队列是否有有效的等待节点,有则返回true,没有则返回false,直接尝试获取锁;非公平实现查看队列中的下一个节点是否是独占模式,是独占模式,则需要阻塞,避免写锁的获取总是发生饥饿,否则,可以直接尝试获取锁。abstract boolean writerShouldBlock();//独占锁获取是否需要阻塞。控制独占模式的获取锁操作是否公平,子类实现。公平实现会看当前同步队列是否有有效的等待节点,有则返回true,需要加入同步队列,顺序获取,没有则返回false,直接尝试获取锁;非公平实现返回false,代表不需要等待可以直接尝试获取锁protected final boolean tryRelease(int releases) {//独占模式的释放锁if (!isHeldExclusively())//如果当前线程不是持有独占锁的线程,将抛出异常throw new IllegalMonitorStateException();int nextc = getState() - releases;boolean free = exclusiveCount(nextc) == 0;//exclusiveCount方法检查释放后的状态的低16位,是0则独占锁完全释放,设置当前持有独占锁的线程位nullif (free)setExclusiveOwnerThread(null);setState(nextc);//因为是独占的释放,所以直接set不会有问题。写volatile,之前的所有内存操作会对所有线程可见,释放之后其他线程才能获取锁。return free;//完全释放独占锁才返回true,这里有可能是锁降级,或者是读写锁完全释放。}protected final boolean tryAcquire(int acquires) {//独占模式的获取锁Thread current = Thread.currentThread();//获取当前线程int c = getState();//获取当前状态int w = exclusiveCount(c);//获取写状态if (c != 0) {//状态不是0,则当前锁已被获取if (w == 0 || current != getExclusiveOwnerThread())//状态不为0,写状态为0,说明读状态不为0。即读锁被获取或者当前线程不是持有独占锁的线程,获取独占锁失败,只有重入的独占锁才能获取return false;if (w + exclusiveCount(acquires) > MAX_COUNT)//写状态超出16位的最大值则失败抛出异常throw new Error("Maximum lock count exceeded");setState(c + acquires);//执行到这里说明状态不为0且写状态不为0,说明独占锁已经被获取,且当前线程是持有独占锁的线程,该获取操作是重入获取独占锁return true;}//当前状态为0if (writerShouldBlock() //写锁是否需要等待,由子类实现,公平实现会看当前同步队列是否有有效的等待节点,有则返回true,需要加入同步队列,顺序获取,没有则返回false,直接尝试获取锁;非公平实现返回false,代表不需要等待可以直接尝试获取锁|| !compareAndSetState(c, c + acquires))//或CAS更新状态失败return false;//不可以直接获取或者CAS更新状态失败,返回falsesetExclusiveOwnerThread(current);return true;//可以获取锁且CAS更新状态成功,设置当前线程为持有独占锁的线程,返回true}protected final boolean tryReleaseShared(int unused) {//共享模式的释放锁Thread current = Thread.currentThread();if (firstReader == current) {if (firstReaderHoldCount == 1)firstReader = null;elsefirstReaderHoldCount--;} else {HoldCounter rh = cachedHoldCounter;if (rh == null || rh.tid != current.getId())rh = readHolds.get();int count = rh.count;if (count <= 1) {readHolds.remove();if (count <= 0)throw unmatchedUnlockException();}--rh.count;}for (;;) {int c = getState();int nextc = c - SHARED_UNIT;//状态减去1左移16位的数,即读状态减一if (compareAndSetState(c, nextc))//CAS更新读状态成功return nextc == 0;//如果更新后的状态位为0,代表读写锁完全释放,返回true。返回true才会唤醒同步队列中的线程}}private IllegalMonitorStateException unmatchedUnlockException() {return new IllegalMonitorStateException("attempt to unlock read lock, not locked by current thread");}protected final int tryAcquireShared(int unused) {//共享模式的获取锁Thread current = Thread.currentThread();int c = getState();if (exclusiveCount(c) != 0 &&getExclusiveOwnerThread() != current)//独占锁被持有,且当前线程不是持有独占锁的线程,返回不可获取return -1;int r = sharedCount(c);//读状态if (!readerShouldBlock() &&r < MAX_COUNT &&compareAndSetState(c, c + SHARED_UNIT)) {//如果读锁不需要阻塞,且读状态小于最大值,且读状态CAS增加成功,意味着获取共享锁成功if (r == 0) {//如果之前读状态是0,设置firstReader第一个读线程为当前线程,firstReaderHoldCount第一个读线程获取锁计数1firstReader = current;firstReaderHoldCount = 1;} else if (firstReader == current) {//firstReader第一个读线程为当前线程,说明这次是重入的获取共享锁,firstReaderHoldCount第一个读线程获取锁计数+1firstReaderHoldCount++;} else {HoldCounter rh = cachedHoldCounter;//缓存最后一个获取共享锁的线程状态if (rh == null || rh.tid != current.getId())cachedHoldCounter = rh = readHolds.get();else if (rh.count == 0)readHolds.set(rh);rh.count++;//readHolds保存每个线程持有的读锁计数}return 1;//获取成功}//不可获取return fullTryAcquireShared(current);}final int fullTryAcquireShared(Thread current) {HoldCounter rh = null;for (;;) {int c = getState();if (exclusiveCount(c) != 0) {//独占锁被持有if (getExclusiveOwnerThread() != current)//当前线程不是持有独占锁的线程。返回-1return -1;} else if (readerShouldBlock()) {//如果需要阻塞if (firstReader == current) {} else {if (rh == null) {rh = cachedHoldCounter;if (rh == null || rh.tid != current.getId()) {rh = readHolds.get();if (rh.count == 0)readHolds.remove();}}if (rh.count == 0)return -1;}}if (sharedCount(c) == MAX_COUNT)//共享锁获取已经达到最大计数throw new Error("Maximum lock count exceeded");if (compareAndSetState(c, c + SHARED_UNIT)) {//CAS增加读状态成功if (sharedCount(c) == 0) {//如果之前读状态是0,设置firstReader第一个读线程为当前线程,firstReaderHoldCount第一个读线程获取锁计数1firstReader = current;firstReaderHoldCount = 1;} else if (firstReader == current) {//firstReader第一个读线程为当前线程,说明这次是重入的获取共享锁,firstReaderHoldCount第一个读线程获取锁计数+1firstReaderHoldCount++;} else {if (rh == null)rh = cachedHoldCounter;if (rh == null || rh.tid != current.getId())rh = readHolds.get();else if (rh.count == 0)readHolds.set(rh);rh.count++;cachedHoldCounter = rh; // cache for release}return 1;//获取成功}}}final boolean tryWriteLock() {//仅当写入锁在调用期间未被另一个线程保持时获取该锁Thread current = Thread.currentThread();int c = getState();if (c != 0) {//状态不为0int w = exclusiveCount(c);if (w == 0 || current != getExclusiveOwnerThread())//写状态为0或者当前线程不是持有独占锁的线程return false;if (w == MAX_COUNT)throw new Error("Maximum lock count exceeded");}//写状态不为0,且当前线程是持有独占锁的线程if (!compareAndSetState(c, c + 1))return false;setExclusiveOwnerThread(current);return true;//CAS更新成功,设置当前线程为持有独占锁的线程,返回成功获取}final boolean tryReadLock() {//仅当写入锁在调用期间未被另一个线程保持时获取读取锁。Thread current = Thread.currentThread();for (;;) {int c = getState();if (exclusiveCount(c) != 0 &&getExclusiveOwnerThread() != current)return false;//这种情况说明独占锁被持有,且不是当前线程持有,一定获取不到共享锁int r = sharedCount(c);if (r == MAX_COUNT)//最大值校验throw new Error("Maximum lock count exceeded");if (compareAndSetState(c, c + SHARED_UNIT)) {//循环CAS直到更新状态成功,获取到共享锁if (r == 0) {firstReader = current;firstReaderHoldCount = 1;} else if (firstReader == current) {firstReaderHoldCount++;} else {HoldCounter rh = cachedHoldCounter;if (rh == null || rh.tid != current.getId())cachedHoldCounter = rh = readHolds.get();else if (rh.count == 0)readHolds.set(rh);rh.count++;}return true;}}}protected final boolean isHeldExclusively() {//当前线程是否是持有独占锁的线程return getExclusiveOwnerThread() == Thread.currentThread();}// Methods relayed to outer classfinal ConditionObject newCondition() {//独占锁可以获取等待队列return new ConditionObject();}final Thread getOwner() {//获取当前持有独占锁的线程return ((exclusiveCount(getState()) == 0) ?null :getExclusiveOwnerThread());}final int getReadLockCount() {//共享锁计数return sharedCount(getState());}final boolean isWriteLocked() {//独占锁是否被持有return exclusiveCount(getState()) != 0;}final int getWriteHoldCount() {//获取当前线程持有的写锁计数return isHeldExclusively() ? exclusiveCount(getState()) : 0;}final int getReadHoldCount() {//获取当前线程获取共享锁的重入计数if (getReadLockCount() == 0)return 0;Thread current = Thread.currentThread();if (firstReader == current)return firstReaderHoldCount;HoldCounter rh = cachedHoldCounter;if (rh != null && rh.tid == current.getId())return rh.count;int count = readHolds.get().count;if (count == 0) readHolds.remove();return count;}/*** Reconstitute this lock instance from a stream* @param s the stream*/private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();readHolds = new ThreadLocalHoldCounter();setState(0); // reset to unlocked state}final int getCount() { return getState(); }//获取总读写状态}

NonfairSync

    static final class NonfairSync extends Sync {private static final long serialVersionUID = -8159625535654395037L;final boolean writerShouldBlock() {return false; // writers can always barge}final boolean readerShouldBlock() {/* As a heuristic to avoid indefinite writer starvation,* block if the thread that momentarily appears to be head* of queue, if one exists, is a waiting writer.  This is* only a probabilistic effect since a new reader will not* block if there is a waiting writer behind other enabled* readers that have not yet drained from the queue.*/return apparentlyFirstQueuedIsExclusive();}}final boolean apparentlyFirstQueuedIsExclusive() {Node h, s;return (h = head) != null &&(s = h.next)  != null &&!s.isShared()         &&s.thread != null;}

FairSync

    static final class FairSync extends Sync {private static final long serialVersionUID = -2274990926593161451L;final boolean writerShouldBlock() {return hasQueuedPredecessors();}final boolean readerShouldBlock() {return hasQueuedPredecessors();}}public final boolean hasQueuedPredecessors() {// The correctness of this depends on head being initialized// before tail and on head.next being accurate if the current// thread is first in queue.Node t = tail; // Read fields in reverse initialization orderNode h = head;Node s;return h != t &&((s = h.next) == null || s.thread != Thread.currentThread());}

使用方式

public class ReadWriteLockTest {private final Map<String, String> m = new TreeMap<String, String>();private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();private final Lock r = rwl.readLock();private final Lock w = rwl.writeLock();public String get(String key) {r.lock();try {return m.get(key);} finally {r.unlock();}}public String put(String key, String value) {w.lock();try {return m.put(key, value);} finally {w.unlock();}}public void clear() {w.lock();try {m.clear();} finally {w.unlock();}}}

JUC - ReentrantReadWriteLock 源码分析相关推荐

  1. 【Java并发编程】16、ReentrantReadWriteLock源码分析

    一.前言 在分析了锁框架的其他类之后,下面进入锁框架中最后一个类ReentrantReadWriteLock的分析,它表示可重入读写锁,ReentrantReadWriteLock中包含了两种锁,读锁 ...

  2. Java并发-ReentrantReadWriteLock源码分析

    ReentrantLock实现了标准的互斥重入锁,任一时刻只有一个线程能获得锁.考虑这样一个场景:大部分时间都是读操作,写操作很少发生:我们知道,读操作是不会修改共享数据的,如果实现互斥锁,那么即使都 ...

  3. Java线程池(3)- JUC Executors 源码分析

    4.JUC Executors 使用&源码分析 未完待续,写作中- 1.JUC里有几种线程池?各自的使用场景? FixedThreadPool public static ExecutorSe ...

  4. 读写锁ReentrantReadWriteLock源码分析

    文章目录 读写锁的介绍 写锁详解 写锁的获取 写锁的释放 读锁详解 读锁的获取 读锁的释放 锁降级 读写锁的介绍 在并发场景中用于解决线程安全的问题,我们几乎会高频率的使用到独占式锁,通常使用java ...

  5. 喻红叶《Java并发-ReentrantReadWriteLock源码分析》

    ReentrantLock实现了标准的互斥重入锁,任一时刻只有一个线程能获得锁.考虑这样一个场景:大部分时间都是读操作,写操作很少发生:我们知道,读操作是不会修改共享数据的,如果实现互斥锁,那么即使都 ...

  6. Java多线程 -- JUC包源码分析2 -- Copy On Write/CopyOnWriteArrayList/CopyOnWriteArraySet

    本人新书出版,对技术感兴趣的朋友请关注: https://mp.weixin.qq.com/s/uq2cw2Lgf-s4nPHJ4WH4aw 上1篇讲述了Java并发编程的第1个基本思想–CAS/乐观 ...

  7. JUC AQS ReentrantLock源码分析

    Java的内置锁一直都是备受争议的,在JDK 1.6之前,synchronized这个重量级锁其性能一直都是较为低下,虽然在1.6后,进行大量的锁优化策略,但是与Lock相比synchronized还 ...

  8. 【JUC】JDK1.8源码分析之ArrayBlockingQueue(三)

    一.前言 在完成Map下的并发集合后,现在来分析ArrayBlockingQueue,ArrayBlockingQueue可以用作一个阻塞型队列,支持多任务并发操作,有了之前看源码的积累,再看Arra ...

  9. synchronousqueue场景_【JUC】JDK1.8源码分析之SynchronousQueue(九)

    一.前言 本篇是在分析Executors源码时,发现JUC集合框架中的一个重要类没有分析,SynchronousQueue,该类在线程池中的作用是非常明显的,所以很有必要单独拿出来分析一番,这对于之后 ...

最新文章

  1. PyTorch官方培训教程上线:从基本概念到实操,小白也能上手
  2. .c/.cpp文件形成.exe文件的过程
  3. 【SDOI2017】天才黑客
  4. webservice小坑
  5. 探秘 Linux 之父 Linus Torvalds 的日常,一不小心就撸出两个创世神作!
  6. IT服务外包的必要性
  7. SQL连接MYSQL出现对象名无效_在SQL数据库中创建视图为什么执行时显示对象名无效?...
  8. 桌面图标全部成被选中状态解决办法
  9. 知识百科:针式打印机打印头是核心技术
  10. 资产管理3大重要性,你还不知道吗?
  11. 不是一番寒彻骨,哪得梅花扑鼻香
  12. 玩游戏显示计算机丢失,win7玩游戏提示缺少D3DCompiler_47.dll文件怎么处理
  13. soul_admin之使用zookeeper数据同步
  14. android 角标最新设置方案
  15. Node.js 使用expresss,ejs模板引擎实现简单的登录注册
  16. 【第一性原理】Centos7下编译并行lammps-7Aug19
  17. FFT从入门到使用(ACM/OI)
  18. Linux进程管理进程优先级nice
  19. BFS算法之求单源最短路径
  20. keysight-34461A使用感受

热门文章

  1. 分享几个ip定位api
  2. 【OJ比赛日历】快周末了,不来一场比赛吗? #11.26-12.02 #13场
  3. 通过异常分数表实现微服务系统根因节点查找(包含代码示例)
  4. R语言实战读书笔记(四)基本数据管理
  5. python购物车结算不了_python第八天)——购物车作业优化完成
  6. 无线抄表系统服务器,无线集中抄表系统设计与实现
  7. 工厂设备管理难点与解决方案
  8. .NET Framework 3.5弹出错误代码0x800f0954解决方法
  9. 计算机个人医院履职总结报告,医院统计个人工作总结范文
  10. 39个前端精美后台模板(简单实用)