Java并发编程之ReentrantReadWriteLock(二)

52 阅读3分钟

书接上文Java并发编程之ReentrantReadWriteLock(一),这篇继续介绍加锁的第三步

尝试加锁

// ReentrantReadWriteLock.Sync
final int fullTryAcquireShared(Thread current) {
    HoldCounter rh = null;
    for (;;) {
        int c = getState();
        if (exclusiveCount(c) != 0) {// 有写锁                                   // 第一部分
            if (getExclusiveOwnerThread() != current)
                return -1;
        } else if (readerShouldBlock()) {// 没有写锁,但是队列第二个是加写锁       // 第二部分
            if (firstReader == current) {
                
            } 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;
        }
    }
}

这个方法主体部分是一个死循环,分为四部分分别进行介绍

第一部分

首先获取独占锁的数量,如果为0表示没有线程正在加写锁,就执行第二部分;不为0表示有线程加了写锁,再检查持有锁的线程是不是当前线程,如果不是就加锁失败,返回-1;否则表示当前线程加了写锁,就执行到第三部分去尝试获取读锁,这也就是锁降级的表现。

第二部分
  • 检查队列中第二个节点是否是独占锁模式,不是的话进入第三部分尝试去获取锁;

  • 是的话检查第一个获取读锁的线程是不是当前线程,如果是进入第三部分尝试去获取重入读锁;

  • 否则进入else部分,获取缓存的最后一次成功获取读锁的数量,

    • 如果是null表示没有其他线程成功获取过读锁,或者最后一次获取成功读锁的线程不是当前线程,就获取当前线程持有锁数量;
    • 如果为0表示当前线程没加过读锁,清除计数器然后返回-1表示加锁失败。
第三部分

如果共享锁数量达到最大值,抛出异常,否则进入第四部分。

第四部分

尝试加共享锁,如果失败了就重新回到for循环一直重试;加锁成功的话:

  • 如果共享锁数量是0,当前线程就是第一个加读锁的线程,修改firstReaderfirstReaderHoldCount的值;
  • 如果共享锁数量不为0但是第一个加锁的线程是当前线程,就直接在firstReaderHoldCount上加1
  • 如果最后一个持有锁的线程不是当前线程,就更新当前线程持有锁数量,然后缓存到cachedHoldCounter;如果最后一个线程是当前线程,就拿缓存的数量更新到线程本地readHolds,然后对count1并更新缓存。