此处源码分析,主要是基于读锁,非公平机制,JDK1.8。

问题:

1、ReentrantReadWriteLock是如何创建读锁与写锁?

2、读锁与写锁的区别是什么?

3、锁的重入次数与获取锁的线程数分别是用哪种方式记录的?

4、当队列中出现多个共享模式的线程节点连续排列时,那么当第一个共享模式的线程拿到锁之后,后面的共享线程节点怎么获取锁?

一、创建ReadLock。

  1. ReentrantReadWriteLock rrw = new ReentrantReadWriteLock();
  1. public ReentrantReadWriteLock(boolean fair) {
  2. sync = fair ? new FairSync() : new NonfairSync();
  3. readerLock = new ReadLock(this);
  4. writerLock = new WriteLock(this);
  5. }
  1. rrw.readLock().lock();

1、当fair的值为false时,非公平的方式创建锁,当fair的值为true时,公平的方式创建锁。

2、初始化readerLock与writerLock,这两个变量是ReentrantReadWriteLock的内部变量。

3、sync执行非公平的锁。

二、lock()源码分析

2.1、sync.acquireShared(1)

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

(1)tryAcquireShared的作用是当前线程获取读锁,当返回1时,表示获取成功,-1表示获取失败。

(2)doAcquireShared,表示获取失败的时候调用。将获取失败的线程加入到等待队列中,并调用LockSupport.park方法阻塞住,等待线程释放permit。

2.2、tryAcquireShared(arg)

  1. protected final int tryAcquireShared(int unused) {
  2.  
  3. Thread current = Thread.currentThread();
  4. // 获取到占有锁的线程数
  5. int c = getState();
  6. // 如果写锁被占领了且不是当前线程占领,那么直接返回 -1
  7. if (exclusiveCount(c) != 0 &&
  8. getExclusiveOwnerThread() != current)
  9. return -1;
  10. int r = sharedCount(c); // 占有共享锁的线程数
  11. if (!readerShouldBlock() && // 如果队列的头节点的next节点是独享模式的线程节点即获取写锁的线程节点,返回true
  12. r < MAX_COUNT && // 共享的数据不能超过65535
  13. compareAndSetState(c, c + SHARED_UNIT)) { // cas设置state
  14. if (r == 0) { // 线程来拿读锁,读锁和写锁没有被任何线程拥有,那么r==0
  15. firstReader = current; //
  16. firstReaderHoldCount = 1;
  17. } else if (firstReader == current) { // 如果线程重复获取读锁,那么从这里开始重入
  18. firstReaderHoldCount++;
  19. } else { // 如果读锁被线程x占领,线程y也要来申请读锁,那么分支就走到这里了
  20.  
  21. // HoldCounter类中存储了两个属性,一个是count,用于记录线程的重入次数,一个是tid,记录当前线程的id
  22. HoldCounter rh = cachedHoldCounter;
  23.  
  24. // 线程x拥有读锁之后,线程y第一次申请的时候会走到这里
  25. //cachedHoldCounter 是一个缓存,保存当前操作线程的上一个线程的操作结果。线程y操作完之后,就会保存线程y的信息
  26. // 如果另外一个线程z来获取到读锁的时候,虽然rh!=null,但是rh.tid != getThreadId(current),
  27. //那么会创建一个默认的HoldCounter,并保存到cachedHoldCounter,并且默认的count=0
  28. if (rh == null || rh.tid != getThreadId(current))
  29. // readHolds.get(),查看源码可以知道,在这个方法中包含了数据初始化的过程,会调用ReentrantReadWriteLock.java
  30. // 下面的方法
  31. /**
  32. * public HoldCounter initialValue() {
  33. * return new HoldCounter();
  34. * }
  35. */
  36. cachedHoldCounter = rh = readHolds.get();
  37. else if (rh.count == 0) // 这个分支也会来到,当线程释放锁,但是没有关闭,当再次调用线程时,readHolds中会存在HoldCounter,count=0
  38. readHolds.set(rh);
  39. rh.count++; // 计算重入的次数
  40. }
  41. return 1;
  42. }
  43. return fullTryAcquireShared(current);
  44. }

请注意:

(1)ReentrantReadWriteLock中维持了一个类ThreadLocalHoldCounter,这个类会生成一个map,key是线程的id,value是HoldCounter对象,HoldCounter对象如下:

  1. static final class HoldCounter {
  2. int count = 0;
  3. // Use id, not reference, to avoid garbage retention
  4. final long tid = getThreadId(Thread.currentThread());
  5. }

其中count就是线程的重入次数,tid就是当前线程的id。这个是与ReentrantLock区别的地方。

(2)ReentrantReadWriteLock使用32位int类型来表示占有锁的线程数,其中高16位是获取到读锁的线程数,低16位是获取到写锁的线程数,提供了计算线程数的方法。

  1. static final int SHARED_SHIFT = 16;(1
  2. static final int SHARED_UNIT = (1 << SHARED_SHIFT);(2
  3. static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;(3
  4. static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;(4
  5.  
  6. /** Returns the number of shared holds represented in count */
  7. static int sharedCount(int c) { return c >>> SHARED_SHIFT; }(5
  8. /** Returns the number of exclusive holds represented in count */
  9. static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }(6
  10. c + SHARED_UNIT

其中(1)是共享移动常量;(2)是共享添加的常量;(3)是最大线程数65535(也就是11111111 11111111);(4)跟(3)一样;(5)计算共享线程数,把c的值向右移16为,并且高位补0; >>> 无符号右移,高位补0;(6)计算独享的线程数,把c的值与11111111 11111111 按位与,这样其实就是取到了写锁的线程数;(7)是共享线程+1。

源码分析:

2.2.1、readerShouldBlock()

这个方法的作用是把判断当前获取读锁的线程是否需要阻塞,条件是:在等待队列中头节点的下一个节点是独享模式的线程。

  1. // 读锁应该被阻塞
  2. final boolean readerShouldBlock() {
  3. return apparentlyFirstQueuedIsExclusive();
  4. }
  5.  
  6. /**
  7. * Returns {@code true} if the apparent first queued thread, if one
  8. * exists, is waiting in exclusive mode. If this method returns
  9. * {@code true}, and the current thread is attempting to acquire in
  10. * shared mode (that is, this method is invoked from {@link
  11. * #tryAcquireShared}) then it is guaranteed that the current thread
  12. * is not the first queued thread. Used only as a heuristic in
  13. * ReentrantReadWriteLock.
  14. *如果第一个入队列的线程节点存在,并且工作在独享模式下,那么返回true;
  15. *如果这个方法返回true,并且当前线程以共享的模式获取锁,这个方法保证了它不是第一个入队列的
  16. *(读锁与读锁都是共存的,所以不会入队,只有当队列中有独享模式的线程节点的时候,获取共享模式的线程才会加入到队列中。)
  17. */
  18. final boolean apparentlyFirstQueuedIsExclusive() {
  19. Node h, s;
  20. // 头节点存在,并且存在下一个节点,下一个节点是独享模式,下一个节点的thread不是空,则返回true
  21. return (h = head) != null &&
  22. (s = h.next) != null &&
  23. !s.isShared() &&
  24. s.thread != null;
  25. }

2.2.2、fullTryAcquireShared(current)

这个方法的作用与tryAcquireShared的作用很类似。

  1. // 进入这个方法的条件,
  2. /**条件1:!readerShouldBlock() && // 如果第一个入队列的线程节点存在,并且工作在独享模式下,那么返回true;
  3. * 条件2:r < MAX_COUNT && // 共享的数据不能超过65535,读锁的线程数已经超过了65535
  4. * 条件3:compareAndSetState(c, c + SHARED_UNIT) // 两个竞争读锁的线程都运行到这里,第一个竞争成功,那么第二个就会竞争失败,返回false
  5. * 其实这个方法分别对这三种状态进行处理
  6. */
  7. /**
  8. * Full version of acquire for reads, that handles CAS misses
  9. * and reentrant reads not dealt with in tryAcquireShared.
  10. */
  11. final int fullTryAcquireShared(Thread current) {
  12. /*
  13. * This code is in part redundant with that in
  14. * tryAcquireShared but is simpler overall by not
  15. * complicating tryAcquireShared with interactions between
  16. * retries and lazily reading hold counts.
  17. */
  18. HoldCounter rh = null;
  19. for (;;) {
  20. int c = getState();
  21. // 如果排他锁被别的线程拿了,直接返回-1
  22. if (exclusiveCount(c) != 0) {
  23. if (getExclusiveOwnerThread() != current)
  24. return -1;
  25. // else we hold the exclusive lock; blocking here
  26. // would cause deadlock.
  27. } else if (readerShouldBlock()) { // 这里是对条件1的处理
  28. // 如果队列的头的下一个节点是请求的排他锁的线程在等待,那么就返回true
  29. // Make sure we're not acquiring read lock reentrantly
  30. if (firstReader == current) {
  31. // assert firstReaderHoldCount > 0;
  32. } else {
  33. if (rh == null) {
  34. rh = cachedHoldCounter;
  35. if (rh == null || rh.tid != getThreadId(current)) {
  36. rh = readHolds.get();
  37. // 如果当前线程的count==0,也就是说当前线程才进来,没有获取到锁,那么直接把它从readHolds中移除
  38. if (rh.count == 0)
  39. // 移除当前线程的HoldCounter
  40. readHolds.remove();
  41. }
  42. }
  43. // 移除之后,返回-1
  44. if (rh.count == 0)
  45. return -1;
  46. }
  47. }
  48. // 这里是对条件2的处理,直接抛出错误!
  49. if (sharedCount(c) == MAX_COUNT)
  50. throw new Error("Maximum lock count exceeded");
  51. // 这里是对条件3的处理,竞争设置state,如果竞争还是失败,那么就要再循环一次,直到死循环能够跳出去
  52. if (compareAndSetState(c, c + SHARED_UNIT)) {
  53. // 如果共享锁的数量为0
  54. if (sharedCount(c) == 0) {
  55. // 设置第一个线程为当前的线程
  56. firstReader = current;
  57. // 设置HoldCount =1
  58. firstReaderHoldCount = 1;
  59. } else if (firstReader == current) {
  60. firstReaderHoldCount++;
  61. } else {
  62. if (rh == null)
  63. rh = cachedHoldCounter;
  64. if (rh == null || rh.tid != getThreadId(current))
  65. rh = readHolds.get();
  66. else if (rh.count == 0)
  67. readHolds.set(rh);
  68. rh.count++;
  69. cachedHoldCounter = rh; // cache for release
  70. }
  71. return 1;
  72. }
  73. }
  74. }

  

 

  

ReentrantReadWriteLock源码分析(一)的更多相关文章

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

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

  2. ReentrantReadWriteLock 源码分析

    ReentrantReadWriteLock  源码分析: 1:数据结构: 成员变量: private final ReentrantReadWriteLock.ReadLock readerLock ...

  3. Java并发指南10:Java 读写锁 ReentrantReadWriteLock 源码分析

    Java 读写锁 ReentrantReadWriteLock 源码分析 转自:https://www.javadoop.com/post/reentrant-read-write-lock#toc5 ...

  4. Java显式锁学习总结之五:ReentrantReadWriteLock源码分析

    概述 我们在介绍AbstractQueuedSynchronizer的时候介绍过,AQS支持独占式同步状态获取/释放.共享式同步状态获取/释放两种模式,对应的典型应用分别是ReentrantLock和 ...

  5. ReentrantReadWriteLock源码分析笔记

    ReentrantReadWriteLock包含两把锁,一是读锁ReadLock, 此乃共享锁, 一是写锁WriteLock, 此乃排它锁. 这两把锁都是基于AQS来实现的. 下面通过源码来看看Ree ...

  6. ReentrantReadWriteLock 源码分析以及 AQS 共享锁 (二)

    前言 上一篇讲解了 AQS 的独占锁部分(参看:ReentrantLock 源码分析以及 AQS (一)),这一篇将介绍 AQS 的共享锁,以及基于共享锁实现读写锁分离的 ReentrantReadW ...

  7. ReentrantReadWriteLock源码分析及理解

    本文结构 读写锁简介:介绍读写锁.读写锁的特性以及类定义信息 公平策略及Sync同步器:介绍读写锁提供的公平策略以及同步器源码分析 读锁:介绍读锁的一些常用操作和读锁的加锁.解锁的源码分析 写锁:介绍 ...

  8. 多线程之美7一ReentrantReadWriteLock源码分析

    目录 前言 在多线程环境下,为了保证线程安全, 我们通常会对共享资源加锁操作,我们常用Synchronized关键字或者ReentrantLock 来实现,这两者加锁方式都是排他锁,即同一时刻最多允许 ...

  9. Java并发编程笔记之读写锁 ReentrantReadWriteLock 源码分析

    我们知道在解决线程安全问题上使用 ReentrantLock 就可以,但是 ReentrantLock 是独占锁,同时只有一个线程可以获取该锁,而实际情况下会有写少读多的场景,显然 Reentrant ...

随机推荐

  1. mybatis 传参为 Integer 时 ,Mapper 文件 中判断 条件 问题。

    <if test="valiStatus==null || valiStatus=='' || valiStatus==4 "> b.work_permit_card_ ...

  2. 向一个文件流写入一个数据块---fwrite

    函数原型:int fwrite(const void *buffer,size_t size,size_t count,FILE *stream); 参数说明:buffer:用于写入到文件的数据地址. ...

  3. sqlldr 笔记

    表结构 CREATE table sqlloader_test ( f1 char(20), f2 char(20), f3 number(16), f4 date ); 数据文件data.csv 1 ...

  4. Basic4android v3.80 beta 发布

    增加了条件编译,共享模块,部分支持jar 文件直接访问.还有其他一些更新. I'm happy to release B4A v3.80 BETA. This version includes sev ...

  5. DevExpress VCL 已死-----关于13.1.4的发布。

    随着DevExpress VCL 13.1.4 的发布,已基本上宣布了devexpress vcl 已经死亡了. 除了一些bug 修正,没有什么新的东西,每年的订阅费又那么贵,而且delphi 现在已 ...

  6. 使用WebUploader客户端批量上传图片,后台使用springMVC接收实例

    使用WebUploader客户端批量上传图片,后台使用springMVC接收实例 我是搞Java后台的,因为最近主管让用webUploader写客户端,但是在网上找了很多,能够复制就能用的并没有几个, ...

  7. POJ 2728 Desert King (最优比率树)

    题意:有n个村庄,村庄在不同坐标和海拔,现在要对所有村庄供水,只要两个村庄之间有一条路即可,建造水管距离为坐标之间的欧几里德距离,费用为海拔之差,现在要求方案使得费用与距离的比值最小,很显然,这个题目 ...

  8. 前端程序员经常忽视的一个 JavaScript 面试题

    题目 function Foo() { getName = function () { alert (1); }; return this; } Foo.getName = function () { ...

  9. 分享一个以前写的基于C#语言操作数据库的小框架

    一:前言 这个是以前写的操作MySQL数据库的小型框架,如果是中小型项目用起来也是很不错的,里面提供Filter.ModelPart.Relationship等机制实现操作数据库时的SQL语句的拼接和 ...

  10. Learning ROS for Robotics Programming - Second Edition(《ROS机器人编程学习-第二版》)

    Learning ROS for Robotics Programming - Second Edition <ROS机器人编程学习-第二版> ----Your one-stop guid ...