前两篇我们讲述了ReentrantLock的加锁释放锁过程,相对而言比较简单,本篇进入深水区,看看ReentrantReadWriteLock-读写锁的加锁过程是如何实现的,继续拜读老Lea凌厉的代码风。

一、读写锁的类图

    读锁就是共享锁,而写锁是独占锁。读锁与写锁之间的互斥关系为:读读可同时执行(有条件的);读写与写写均互斥执行。注意此处读读可并行我用了有条件的并行,后文会对此做介绍。

    继续奉上一张丑陋的类图:

     可以看到ReentrantReadWriteLock维护了五个内部类,ReentrantReadWriteLock中存放了Sync、ReadLock、WriteLock三个成员变量,如下截图所示:

     而ReadLock和WriteLock中又存放了Sync变量,截图如下所示,这样一组合,有了四种锁,公平读锁、公平写锁、非公平读锁、非公平写锁。对于公平与非公平的实现区别,我们上一篇已经做过讲解,本文将着重关注读锁和写锁的实现区别。

 

二、加锁源码

    在前文中我们知道,ReentrantLock中用state来判断当前锁是否被占用,而读写锁ReentrantReadWriteLock中由于同时存在两种锁,所以老Lea用state的高16位来存放读锁的占用状态以及重入次数,低16位存放写锁的占用状态和重入次数。

1、读锁加锁,即共享锁加锁

复制代码
1 public void lock() { 2             sync.acquireShared(1); // 获取共享锁方法 3         }
复制代码

    上述lock方法中调用的获取共享锁方法是在AbstractQueuedSynchronizer中实现的,代码如下:

复制代码
1 public final void acquireShared(int arg) { 2         if (tryAcquireShared(arg) < 0) 3             doAcquireShared(arg); 4     }
复制代码

    可以看到获取共享锁分成了两步,第一步是尝试获取,如果获取不到再进入if里面执行doAcquireShared方法,下面分别追踪。

1)、tryAcquireShared方法

复制代码
 1 protected final int tryAcquireShared(int unused) {  2             Thread current = Thread.currentThread();  3             int c = getState();  4             // 1.有写锁占用并且不是当前线程,则直接返回获取失败 5             if (exclusiveCount(c) != 0 && 6                 getExclusiveOwnerThread() != current)  7                 return -1;  8             // 执行到这里,有两种情况 没有写锁占用或者是当前线程 9             int r = sharedCount(c); // 获取读锁次数 10             // 2、不应该阻塞则获取锁  @此方法有点意思,需着重讲解,作用:判断读锁是否需要阻塞11             if (!readerShouldBlock() &&12                 r < MAX_COUNT &&13                 compareAndSetState(c, c + SHARED_UNIT)) { 14                 // 如果CAS成功,则将当前线程对应的计数+115                 if (r == 0) { // 如果读锁持有数为0,则说明当前线程是第一个reader,分别给firstReader和firstReaderHoldCount初始化 16                     firstReader = current; 17                     firstReaderHoldCount = 1; 18                 } else if (firstReader == current) { // 如果读锁持有数不为0且当前线程就是firstReader,那么直接给firstReaderHoldCount+1,表示读锁重入 19                     firstReaderHoldCount++; 20                 } else { // 其他情况,即当前线程不是firstReader且还有其他线程持有读锁,则要获取到当前线程对应的HoldCounter,然后给里面的计数+1 21                     HoldCounter rh = cachedHoldCounter; 22                     if<