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;}
复制代码
历史文章推荐
Java Core 「12」ReentrantLock 再探析
Java Core 「11」AQS-AbstractQueuedSynchronizer
Java Core 「10」J.U.C 同步工具类 -2
Java Core 「9」J.U.C 同步工具类 -1
Java Core 「8」字节码增强技术
Java Core 「7」各种不同类型的锁
Java Core「6」反射与 SPI 机制
Java Core「5」自定义注解编程
Java Core「4」java.util.concurrent 包简介
评论