简介

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. Android开发学习之路--Notification之初体验
  2. Linux uptime指令
  3. 本人对于netty框架的一些理解,怎么与网站上的websock建立连接
  4. SQL JOIN连接分类[转]
  5. java做登录时要加锁吗_你用对锁了吗?谈谈 Java “锁” 事
  6. 每日英语:Targeting Grandpa: China’s Seniors Hunger for Ads
  7. 搜索树判断 (25 分)(先序建立二叉树)
  8. Flash | 用几张帧图创建逐帧动画元件并插入场景层的一般步骤
  9. 不知道如何识别图片文字?这个方法用了都说好
  10. c语言相反数补码,求一个数的相反数的补码
  11. 博客优化、收录、RSS技巧
  12. SpringBoot上传大文件并支持中途取消上传
  13. 筛质数—(朴素筛法、埃氏筛法、欧拉筛法(线性筛法))
  14. 公开密钥密码体制(C语言实现RSA加密算法)
  15. Safari浏览器下载知网文献中文乱码
  16. 几行代码,把zip文件直接破解
  17. 提高LCD屏幕刷新效率
  18. 基于springboot农产品交易平台的设计与实现
  19. Latex悬挂缩进——整段缩进or列表符号不缩进,段落缩进
  20. 爱丽丝漫游奇景注释版拿到手了

热门文章

  1. 智慧停车场行业室外停车场实用价值
  2. PHP爬取微信公众号文章(可做为扩展类直接使用)
  3. 深信服AF防火墙(地址转换)服务器映射
  4. ecshop lbi替换为html,ecshop商城网站首页幻灯片替换成自定义js轮播方法
  5. 5G2B、物联网平台和边缘计算的通俗理解
  6. php trip_tags,PHP之字符串函数
  7. 应讯智能-一体机称重重量获取
  8. 移动web端安卓手机上元素中的文字垂直不居中的问题
  9. 建立网络链接编程C语言,用C语言编写一个网络蜘蛛来搜索网上出现的电子邮件地址...
  10. HTML期末大学生网页设计作业----锤子手机 1页