public ReentrantReadWriteLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();readerLock = new ReadLock(this);writerLock = new WriteLock(this);
}

构造方法中会生成读锁和写锁。

其sync中通过一个32位数字state表示当前持有读写锁的数量。

static final int SHARED_SHIFT   = 16;
static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;/** Returns the number of shared holds represented in count  */
static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
/** Returns the number of exclusive holds represented in count  */
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

高16位表示读锁的数量,低16位表示写锁的数量。通过算数右移state16位表示读锁的数量,将state与1左移16位-1的结果按位相与的结果为当前写锁的数量。

加读锁

public void lock() {sync.acquireShared(1);
}public final void acquireShared(int arg) {if (tryAcquireShared(arg) < 0)doAcquireShared(arg);
}

加读锁的时候调用sync的acquireShard()方法,实现在了父类aqs中,在其中,先尝试调用tryAcquireShard()方法。

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)) {if (r == 0) {firstReader = current;firstReaderHoldCount = 1;} else if (firstReader == current) {firstReaderHoldCount++;} else {HoldCounter rh = cachedHoldCounter;if (rh == null || rh.tid != getThreadId(current))cachedHoldCounter = rh = readHolds.get();else if (rh.count == 0)readHolds.set(rh);rh.count++;}return 1;}return fullTryAcquireShared(current);
}

这里首先得到当前state判断当前是否已经存在别的线程获取写锁,如果有,直接返回-1。否则再根据state得到当前的读锁数量,之后判断当前的读锁是否需要阻塞,在公平锁的情况下,如果存在比当前线程更早进入的aqs并阻塞的线程,那么就需要等待,在非公平锁的情况下,则判断当前aqs队列中的线程是否是独占模式(试图加写锁的节点)来判断需不需要阻塞。

如果不需要阻塞,并且当前的读锁的数量小于上限,则通过cas给state的读锁数量加上一,如果这是第一个加读锁的线程,则当前线程记为第一个读者firstReader,并给当前第一个读者所加锁数量加一。每个线程持有的读锁数量通过threadLocal来保存,这里给threadLocal中保存的HoldCounter中的count加上一,并加入threadLocal管理。

之后返回1,表示读锁的加入并没有被阻塞。

否则,则调用fullTryAcquireShard()继续试图获得读锁。

final int fullTryAcquireShared(Thread current) {HoldCounter rh = null;for (;;) {int c = getState();if (exclusiveCount(c) != 0) {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 reentrantlyif (firstReader == current) {// assert firstReaderHoldCount > 0;} else {if (rh == null) {rh = cachedHoldCounter;if (rh == null || rh.tid != getThreadId(current)) {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)) {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 1;}}
}

这个方法中,会不断循环,如果当前已经存在别的线程持有写锁,那么直接返回-1。之后再一次判断当前加读锁是否需要阻塞,如果需要阻塞,则继续判断该线程是否已经持有过读锁,如果没有,则也返回-1。如果此时已经不需要阻塞,则和之前的操作一样加上获取读锁。

如果成功获取读锁的情况下,返回的1,回到aqs的acquireShard()方法,则会直接结束,代表读锁的成功获取,如果返回-1,说明此时已经有别的线程持有写锁或者当前加读锁需要阻塞,并且当前线程并未曾经成功获取读锁。那么则调用doAcquireShard()方法将当前线程加入队列并挂起。

private void doAcquireShared(int arg) {final Node node = addWaiter(Node.SHARED);boolean failed = true;try {boolean interrupted = false;for (;;) {final Node p = node.predecessor();if (p == head) {int r = tryAcquireShared(arg);if (r >= 0) {setHeadAndPropagate(node, r);p.next = null; // help GCif (interrupted)selfInterrupt();failed = false;return;}}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}
}

这里的代码实现在了aqs中。

这里的线程作为share节点加入到队列中并被挂起,当被唤醒的时候会重新调用之前的tryAcquireShard()方法重新试图取得读锁。

加写锁

public void lock() {sync.acquire(1);
}

acquire()还是实现在aqs中。

public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}

首先tryAcquire()实现在了sync中,试图获取写锁。

protected final boolean tryAcquire(int acquires) {Thread current = Thread.currentThread();int c = getState();int w = exclusiveCount(c);if (c != 0) {// (Note: if c != 0 and w == 0 then shared count != 0)if (w == 0 || current != getExclusiveOwnerThread())return false;if (w + exclusiveCount(acquires) > MAX_COUNT)throw new Error("Maximum lock count exceeded");// Reentrant acquiresetState(c + acquires);return true;}if (writerShouldBlock() ||!compareAndSetState(c, c + acquires))return false;setExclusiveOwnerThread(current);return true;
}

首先判断state是否为0,如果非0,则说明已经存在写锁或者读锁,如果当前不存在读锁,或者当前线程并没有持有写锁,那么直接返回false。

否则如果当前线程已经持有写锁,直接给state加上相应的数量即可,并返回true。

之后判断当前加写锁是否需要阻塞,在非公平锁中,加写锁永远不会被阻塞,在公平锁中的判断方式与加读锁一样。

如果不需要阻塞,直接通过cas改变state,并将当前线程作为持有写锁的线程并返回true。

之后回到aqs的acquire()方法,在刚才的tryAcquire()方法中,如果当前已经存在别的线程持有读锁或者写锁,或者在公平锁的情况下aqs队列中已经存在线程等待,那么则会返回false,加读锁失败,构造独占节点加入aqs队列中挂起等待唤醒。

相比加锁,解除锁的步骤就很简单,并且大部分操作都在aqs中完成。

jdk读写锁ReentrantReadWriteLock相关推荐

  1. 并发编程-19AQS同步组件之重入锁ReentrantLock、 读写锁ReentrantReadWriteLock、Condition

    文章目录 J.U.C脑图 ReentrantLock概述 ReentrantLock 常用方法 synchronized 和 ReentrantLock的比较 ReentrantLock示例 读写锁R ...

  2. Java Review - 并发编程_读写锁ReentrantReadWriteLock的原理源码剖析

    文章目录 ReentrantLock VS ReentrantReadWriteLock 类图结构 非公平的读写锁实现 写锁的获取与释放 void lock() void lockInterrupti ...

  3. java 可重入读写锁 ReentrantReadWriteLock 详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt206 读写锁 ReadWriteLock读写锁维护了一对相关的锁,一个用于只 ...

  4. 深入分析实战可重入读写锁ReentrantReadWriteLock

    文章目录 前言 加锁规则 同步原理 源码解析 实战演示 前言 前面我们学习了可重入锁ReentrantLock,可重入锁是一个排他锁,只要不是当前线程访问加锁资源都不能够进入,只能等待锁的释放.当然, ...

  5. 深入理解读写锁ReentrantReadWriteLock

    深入理解读写锁ReentrantReadWriteLock 前言 业务开发中我们可能涉及到读写操作. 面对写和读,对于数据同步,在使用Lock锁和 synchronized关键字同步数据时候,对于读读 ...

  6. 读写锁ReentrantReadWriteLock:读读共享,读写互斥,写写互斥

    JDK1.5之后,提供了读写锁ReentrantReadWriteLock,读写锁维护了一对锁,一个读锁,一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升.在读多写少的情况下,读写 ...

  7. Oracle java官网关于可重入读写锁ReentrantReadWriteLock的解析

    Oracle java官网关于可重入读写锁ReentrantReadWriteLock的解析 1.[原文链接](https://docs.oracle.com/javase/8/docs/api/ja ...

  8. 读写锁 -- ReentrantReadWriteLock

    1. 类继承关系 如图所示, ReadWriteLock 是一个接口,内部由两个Lock 接口组成. public interface ReadWriteLock {Lock readLock();L ...

  9. 读写锁ReentrantReadWriteLock源码分析

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

最新文章

  1. iOS开发那些事--编写OCUnit测试方法-逻辑测试方法
  2. python安装后无法运行任何软件_为啥我按照python安装教程,总说无法启动此程序,因为计算机中丢失?...
  3. fit,fit_generator的使用区别
  4. 异构服务器 微服务_Spring Cloud Alibaba Sidecar 多语言微服务异构
  5. String Problem(HDU-3374)
  6. 【Python3网络爬虫开发实战】 1.5.4-RedisDump的安装
  7. Atom飞行手册翻译: 1.4 小结
  8. java解决斐波那契数列(Fibonacci sequence)
  9. 清华排名首登亚洲第一,今年财务预算300亿
  10. java objective-c,Objective-C基础教程学习笔记(附录)从Java转向Objective-C
  11. 细说分布式Redis架构设计和那些踩过的坑
  12. CMOS Image Sensor的接口硬件设计(DVP/MIPI CSI)
  13. Windows操作系统 快捷键 必备
  14. 定位到excel最后一个非空单元格操作技巧,你一定要知道!(一)
  15. 「电商干货」分销爆单的6个步骤
  16. 前端面试总结之长沙五
  17. 【数学建模入门】数学建模基本知识|如何准备如何备战
  18. 求n的阶乘并显示过程
  19. 数据增强,扩充了数据集,增加了模型的泛化能力
  20. oracle 关键词大全,Oracle 常见关键词

热门文章

  1. SSM项目使用Idea打war包
  2. Docker保存修改后的镜像
  3. 设计模式之不简单的工厂模式(一)
  4. 字符串匹配(一)—— KMP / MP
  5. Python基础班---第一部分(基础)---Python基础知识---计算机组成原理
  6. 浅谈,JavaScript 运行机制和Event Loop
  7. 不知道选择多云还是混合云?先把概念弄清楚吧
  8. 通过Rancher部署并扩容Kubernetes集群基础篇一
  9. React+Webpack+Eslint+Babel构建React脚手架
  10. js中使用new Date(str)创建时间对象不兼容firefox和ie的解决方式