提示:看了 ReadWriteLock 第一篇 才能看这一篇 ,关于 ReadWriteLock 知识点明白上一篇讲的内容应付一般面试没什么问题了。
1. hasQueuedPredecessors
上一篇在获取读共享锁流程中有一个判断 ,
  if (!readerShouldBlock() && 
   复制代码
 
如果 readerShouldBlock 返回 false 那就正常获取锁,如果返回 true 那么就结束获取锁
这里说一下公平锁这个方法的内容:
 // ReentrantReadWriteLock.FairSync#readerShouldBlockfinal boolean readerShouldBlock() {    return hasQueuedPredecessors();}// 判断// 如果有线程在当前线程获取锁之前排队 也就是队列已经有元素了 但不是自己 返回true (有人排队)// 其他情况 返回false(没人排队)public final boolean hasQueuedPredecessors() {        Node t = tail; // 队列尾指针        Node h = head;// 队列头指针        Node s;// 临时变量          return h != t && // 比较head tail 如果队列为空 h != t 为 false              ((s = h.next) == null || // head的next为空 证明队列为空              s.thread != Thread.currentThread() // 如果head有next 也就是第一个等待线程 判断是不是自己 如果是自己 那嘿嘿            );    }
   复制代码
 2. 获取锁后一系列的设置 都有什么可说的
 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++;}
   复制代码
 
firstReader:记录着第一个获取读锁的线程
firstReaderHoldCount: 记录着第一个获取读锁的线程获取锁的次数
 private transient Thread firstReader = null;// 线程private transient int firstReaderHoldCount;// 数量
   复制代码
 
cachedHoldCounter:记录线程 ID+次数
存储在 ThreadLocal 中  (面试弱引用 ThreadLocal 如果能扯到这里 你就赢一半人了)
每个获取读锁的线程都存着
 static final class HoldCounter {    int count = 0;    // Use id, not reference, to avoid garbage retention    final long tid = getThreadId(Thread.currentThread());}
   复制代码
 
readHolds:继承了 ThreadLocal 的类
 static final class ThreadLocalHoldCounter    extends ThreadLocal<HoldCounter> {    // 这里定义了initialValue     // ThreadLocal第一次初始化map会调用这个函数    public HoldCounter initialValue() {        return new HoldCounter();    }}
   复制代码
 
总结: 就是把所有获取读锁的线程+次数 都存着
至于为什么这样存,我不明白作者的心思,或许跟当时 HashMap 为什么用头插法一样,这样记录在释放锁的时候更快?  不晓得了...
3.  看一下读锁的释放过程
 ReadWriteLock rw = new ReentrantReadWriteLock();rw.readLock().unlock();
   复制代码
 
ReentrantReadWriteLock#tryReleaseShared:
 // AQS#releaseSharedpublic final boolean releaseShared(int arg) {    if (tryReleaseShared(arg)) {        // 如果返回true 也就是释放完锁后 当前线程的读锁已经没有了         // 没有的话 做一个doReleaseShared操作 可以简单理解为唤醒其他等待线程(队列里下一个等待的线程)        doReleaseShared();        return true;    }    return false;}protected final boolean tryReleaseShared(int unused) {    // 获取当前线程     Thread current = Thread.currentThread();    // 查看firstReader是不是当前线程  firstReader在这里用到了    if (firstReader == current) {        // assert firstReaderHoldCount > 0;        // 判断firstReaderHoldCount如果等于1 把firstReader置空        // 也就是获取过一次锁         if (firstReaderHoldCount == 1)            firstReader = null;        else             // 否则 就 减1            firstReaderHoldCount--;    } else {        // 如果不是首个获取读锁的线程         HoldCounter rh = cachedHoldCounter;        // 判断cachedHoldCounter是不是当前线程的持有器  如果不是那就从readHolds(ThreadLocal)中获取        if (rh == null || rh.tid != getThreadId(current))            rh = readHolds.get();        // 判断持有锁次数        int count = rh.count;        // <=1 移除ThreadLocal变量  记着 ThreadLocal最后一定要remove否则就是内存泄漏        if (count <= 1) {            readHolds.remove();            if (count <= 0)                throw unmatchedUnlockException();        }        // 如果持有次数很多  那就减1        --rh.count;    }    //     for (;;) {        // state-SHARED_UNIT         // SHARED_UNIT = (1 << SHARED_SHIFT) = 65536        int c = getState();        int nextc = c - SHARED_UNIT;        // cas设置新值         if (compareAndSetState(c, nextc))            // Releasing the read lock has no effect on readers,            // but it may allow waiting writers to proceed if            // both read and write locks are now free.            return nextc == 0;    }}
// private void doReleaseShared() {    //     for (;;) {        Node h = head;        // h != null && h != tail 如果为true 表示队列中有等待线程        if (h != null && h != tail) {            // 获取waitStatus waitStatus状态在之前文章中有提到过 是他后继线程给他的状态             int ws = h.waitStatus;            // 如果状态是SIGNAL 需要唤醒             if (ws == Node.SIGNAL) {                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))                    continue;            // loop to recheck cases                // 唤醒                 unparkSuccessor(h);            }            // 如果状态是0 就是不需要唤醒  设置为PROPAGATE状态 接着往后唤醒需要唤醒的人            else if (ws == 0 &&                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))                continue;                // 如果cas失败了 接着循环 loop on failed CAS        }        // 如果head变换了 需要接着循环         if (h == head)                   // loop if head changed            break;    }}
   复制代码
 4.  注意
写这些知识为了抛砖,也不是很完善  , 其实可以自己写一个方法 跟着断点,进源码看看 那样更容易加深印象。
关于写锁的获取与释放 读者朋友自行研究吧 有问题可以给我留言 大家一起讨论哈
 
也可以微信公众号留言:木子的昼夜编程 
评论