类结构

内部抽象类Sync继承了AQS
image.png

state

  1. static final int SHARED_SHIFT = 16;
  2. static final int SHARED_UNIT = (1 << SHARED_SHIFT);
  3. static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
  4. static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
  5. //拥有读锁的线程数,c就是state
  6. static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
  7. //独占锁的重入次数
  8. static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方。
image.png

写锁:

获取写锁的代码:写锁只支持一个线程重入,而且读写互斥

  1. protected final boolean tryAcquire(int acquires) {
  2. //当前线程
  3. Thread current = Thread.currentThread();
  4. //获取state状态 存在读锁或者写锁,状态就不为0
  5. int c = getState();
  6. //获取写锁的重入数
  7. int w = exclusiveCount(c);
  8. //当前同步状态state != 0,说明已经有其他线程获取了读锁或写锁
  9. if (c != 0) {
  10. // c!=0 && w==0 表示存在读锁
  11. // 当前存在读锁或者写锁已经被其他写线程获取,则写锁获取失败
  12. if (w == 0 || current != getExclusiveOwnerThread())
  13. return false;
  14. // 超出最大范围 65535
  15. if (w + exclusiveCount(acquires) > MAX_COUNT)
  16. throw new Error("Maximum lock count exceeded");
  17. //同步state状态
  18. setState(c + acquires);
  19. return true;
  20. }
  21. // writerShouldBlock有公平与非公平的实现, 非公平返回false,会尝试通过cas加锁
  22. //c==0 写锁未被任何线程获取,当前线程是否阻塞或者cas尝试获取锁
  23. if (writerShouldBlock() ||
  24. !compareAndSetState(c, c + acquires))
  25. return false;
  26. //设置写锁为当前线程所有
  27. setExclusiveOwnerThread(current);
  28. return true;
  29. }

读锁:

获取读锁的代码:读锁共享,读读不互斥;读锁可重入,每个获取读锁的线程都会记录对应的重入数;
读写互斥,写锁降级场景除外
(写锁降级:持有写锁的线程,可以获取读锁,但是后续要记得把读锁和写锁读释 放)

protected final int tryAcquireShared(int unused) {
    Thread current = Thread.currentThread();
    int c = getState();
    // 如果写锁已经被获取并且获取写锁的线程不是当前线程,当前线程获取读锁失败返回‐1 判
    断锁降级
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
    return ‐1;
    //计算出读锁的数量
    int r = sharedCount(c);
    /**
    * 读锁是否阻塞 readerShouldBlock()公平与非公平的实现
    * r < MAX_COUNT: 持有读锁的线程小于最大数(65535)
    * compareAndSetState(c, c + SHARED_UNIT) cas设置获取读锁线程的数量
    */
    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);
}

读锁重入

上述可知ReentrantReadWriteLock的读锁可重入,那么怎么记录重入次数?
用ThreadLocalHoldCounter
image.png
HoldCounter是用来记录一个线程的读锁重入数
ThreadLocalHoldCounter是ThreadLocal变量,用来存放不是第一个获取读锁的线 程的其他线程的读锁重入数对象