Java Core 「13」ReentrantReadWriteLock 再探析

181 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情

01-ReentrantReadWriteLock.ReadLock#lock

ReentrantReadWriteLock#readLock 来获取读写锁中的读锁,写锁的使用规则为:

  • 若写锁未被其他线程持有,则当前线程可以立即获得读锁;
  • 否则,当前线程被阻塞,直至能够获取到读锁
  • 若当前线程持有写锁,同时请求

ReentrantReadWriteLock.ReadLock#lock → AbstractQueuedSynchronizer#acquireShared

public final void acquireShared(int arg) {
    /** 若可用的共享锁数量小于0,则阻塞当前尝试获取共享读锁的线程 */
    if (tryAcquireShared(arg) < 0)
	/** 将当前线程封装到 Node 中,并加入到 sync 队列队尾 */
        doAcquireShared(arg);
}
/** 这段代码与分析 ReentantLock 时的 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 效果类似 */

→ ReentrantReadWriteLock.Sync#tryAcquireShared

protected final int tryAcquireShared(int unused) {
  Thread current = Thread.currentThread();
  int c = getState();
  /** 如果写锁不空闲,而且持互斥写锁的线程非当前线程,阻塞当前线程 */
  if (exclusiveCount(c) != 0 &&
      getExclusiveOwnerThread() != current)
      return -1;
  /** 下面的情况为:a) 持写锁的线程数量为0,或 b) 持写锁的线程数量不为0,但持锁线程为当前线程 */
  int r = sharedCount(c);
  /** 公平锁和非公平锁在这里的实现不太一样:
   * a) 公平锁:sync 中有其他线程正在等待,则返回 true(当前线程不能获共享读锁);
   * 否则,返回 false(当前线程可以用 CAS 尝试获取共享读锁)
   * b) 非公平锁:sync 中第一个线程等待获取互斥写锁,则返回 true(当前线程不能获得共享读锁);
   * 否则,返回 false(当前线程可以用 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 != LockSupport.getThreadId(current))
              cachedHoldCounter = rh = readHolds.get();
          else if (rh.count == 0)
              readHolds.set(rh);
          rh.count++;
      }
      return 1;
  }
  /** 完整的尝试获取共享读锁的方法,处理了上述所有情况,以及上述未处理的情况,例如重入 */
  return fullTryAcquireShared(current);
}

02-ReentrantReadWriteLock.ReadLock#unlock

ReentrantReadWriteLock.ReadLock#unlock → AbstractQueuedSynchronizer#releaseShared

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
	/** 读锁、写锁都空闲时,尝试唤醒 writer */
        doReleaseShared();
        return true;
    }
    return false;
}

→ ReentrantReadWriteLock.Sync#tryReleaseShared

protected final boolean tryReleaseShared(int unused) {
    Thread current = Thread.currentThread();
    if (firstReader == current) {
        // assert firstReaderHoldCount > 0;
        if (firstReaderHoldCount == 1)
            firstReader = null;
        else
            firstReaderHoldCount--;
    } else {
        HoldCounter rh = cachedHoldCounter;
        if (rh == null ||
            rh.tid != LockSupport.getThreadId(current))
            rh = readHolds.get();
        int count = rh.count;
        if (count <= 1) {
            readHolds.remove();
            if (count <= 0)
                throw unmatchedUnlockException();
        }
        --rh.count;
    }
    /** 主要内容在这里,如果 state == 0,即读锁、写锁都空闲,则唤醒等待的 writer */
    for (;;) {
        int c = getState();
        int nextc = c - SHARED_UNIT;
        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;
    }
}

03-ReentrantReadWriteLock.WriteLock#lock

ReentrantReadWriteLock#writeLock 来获取读写锁中的写锁,写锁的使用规则为:

  • 若读锁、写锁均未被其他线程拥有,则当前线程立即获得写锁;
  • 否则,当前线程阻塞。
  • 若当前线程已获得写锁,则可以再次获得写锁(重入)。
  • 若当前线程已获得读锁,则不可获得写锁,阻塞当前线程。

ReentrantReadWriteLock.WriteLock#lock → AbstractQueuedSynchronizer#acquire → ReentrantReadWriteLock.Sync#tryAcquire

protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    /** ReentrantReadWriteLock 中,读锁与写锁的持锁线程数量在同一个值 getState() 中 
     * 低位部分表示持互斥写锁的线程数量
     * 高位部分表示持共享读锁的线程数量
     */
    int c = getState();
    int w = exclusiveCount(c);
    if (c != 0) { /** 说明读锁或写锁非空闲 */
        // (Note: if c != 0 and w == 0 then shared count != 0)
        if (w == 0 || current != getExclusiveOwnerThread())
	    /** w == 0,说明有人持有读锁,所以获取写锁失败 */
            /** w != 0,说明有人持有读锁,若不是当前线程,则获取写锁失败 */
            return false;
	/** 有人持有写锁,且持锁线程为当前线程,当前线程获锁成功(可重入) */
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        // Reentrant acquire
        setState(c + acquires);
        return true;
    }
    /** 读锁和写锁都空闲时,公平锁和非公平锁在这里的处理有点不同
     * 公平锁,如果 sync 队列中有其他线程等待,则当前线程获取锁失败;
     * 否则,只要 CAS 更新 state 成功则获取锁成功;
     * 非公平锁,writerShouldBlock() 返回值一直为 false;
     * 即不管 sync 队列,只要 CAS 更新 state 成功,则获取锁成功。
     */
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    setExclusiveOwnerThread(current);
    return true;
}

从上面的源码可以看到,出了某些地方需要考虑共享读锁的情况,其他部分与 ReentrantLock 这种互斥锁基本一致。

04-ReentrantReadWriteLock.WriteLock#unlock

写锁的 unlock 与 ReentrantLock 的 unlock 类似。ReentrantReadWriteLock.WriteLock#unlock → AbstractQueuedSynchronizer#release → ReentrantReadWriteLock.Sync#tryRelease

protected final boolean tryRelease(int releases) {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    /** 持有写锁的线程才可释放写锁 */
    int nextc = getState() - releases;
    boolean free = exclusiveCount(nextc) == 0;
    if (free)
        setExclusiveOwnerThread(null);
    setState(nextc);
    /** 如果持有写锁的线程数量为0,则返回 true,
     * AbstractQueuedSynchronizer#release 会唤醒 sync 队列中的 reader
     */
    return free;
}

历史文章推荐