锁专题(六)这么用心的可重入读写锁讲解,不给作者点个赞吗?( 四 )

读锁的释放清单6:读锁释放入口// ReadLockpublic void unlock() {sync.releaseShared(1);}// Syncpublic final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared(); // 这里实际上是释放读锁后唤醒写锁的线程操作return true;}return false;}读锁的释放主要是tryReleaseShared(arg)函数 , 因此拆解其步骤如下:
操作1:清理ThreadLocal中保存的获取锁数量信息
操作2:CAS修改读锁个数 , 实际上是自减一
清单7:读锁的释放流程protected final boolean tryReleaseShared(int unused) {Thread current = Thread.currentThread();// 操作1:清理ThreadLocal对应的信息if (firstReader == current) {;if (firstReaderHoldCount == 1)firstReader = null;elsefirstReaderHoldCount--;} else {HoldCounter rh = cachedHoldCounter;if (rh == null || rh.tid != getThreadId(current))rh = readHolds.get();int count = rh.count;// 已释放完的读锁的线程清空操作if (count <= 1) {readHolds.remove();// 如果没有获取锁却释放则会报该错误if (count <= 0)throw unmatchedUnlockException();}--rh.count;}// 操作2:循环中利用CAS修改读锁状态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;}}写锁的获取清单8:写锁的获取入口// WriteLockpublic void lock() {sync.acquire(1);}// AQSpublic final void acquire(int arg) {// 尝试获取 , 获取失败后入队 , 入队失败则interrupt当前线程if (!tryAcquire(arg) }写锁的获取也主要是tryAcquire(arg)方法 , 这里也拆解步骤:
操作1:如果读锁数量不为0或者写锁数量不为0 , 并且不是重入操作 , 则获取失败 。
操作2:如果当前锁的数量为0 , 也就是不存在操作1的情况 , 那么该线程是有资格获取到写锁 , 因此修改状态 , 设置独占线程为当前线程
清单9:写锁的获取protected final boolean tryAcquire(int acquires) {Thread current = Thread.currentThread();int c = getState();int w = exclusiveCount(c);// 操作1:c != 0 , 说明存在读锁或者写锁if (c != 0) {// (Note: if c != 0 and w == 0 then shared count != 0)// 写锁为0 , 读锁不为0 或者获取写锁的线程并不是当前线程 , 直接失败if (w == 0 || current != getExclusiveOwnerThread())return false;if (w + exclusiveCount(acquires) > MAX_COUNT)throw new Error("Maximum lock count exceeded");// Reentrant acquire// 执行到这里说明是写锁线程的重入操作 , 直接修改状态 , 也不需要CAS因为没有竞争setState(c + acquires);return true;}// 操作2:获取写锁 , writerShouldBlock对于非公平模式直接返回fasle , 对于公平模式则线程需要排队 , 因此需要阻塞 。if (writerShouldBlock() ||!compareAndSetState(c, c + acquires))return false;setExclusiveOwnerThread(current);return true;}写锁的释放清单10:写锁的释放入口// WriteLockpublic void unlock() {sync.release(1);}// AQSpublic final boolean release(int arg) {// 释放锁成功后唤醒队列中第一个线程if (tryRelease(arg)) {Node h = head;if (h != nullreturn true;}return false;}写锁的释放主要是tryRelease(arg)方法 , 其逻辑就比较简单了 , 注释很详细 。
清单11:写锁的释放protected final boolean tryRelease(int releases) {// 如果当前线程没有获取写锁却释放 , 则直接抛异常if (!isHeldExclusively())throw new IllegalMonitorStateException();// 状态变更至nextcint nextc = getState() - releases;// 因为写锁是可以重入 , 所以在都释放完毕后要把独占标识清空boolean free = exclusiveCount(nextc) == 0;if (free)setExclusiveOwnerThread(null);// 修改状态setState(nextc);return free;}锁降级操作哪里体现?锁降级操作指的是一个线程获取写锁之后再获取读锁 , 然后读锁释放掉写锁的过程 。
在tryAcquireShared(arg)获取读锁的代码中有如下代码 。
清单12:写锁降级策略Thread current = Thread.currentThread();// 当前状态int c = getState();// 存在写锁 , 并且写锁不等于当前线程时返回 , 换句话说等写锁为当前线程时则可以继续往下获取读锁 。 if (exclusiveCount(c) != 0 那么锁降级有什么用?答案是为了可见性的保证 。
在ReentrantReadWriteLock的javadoc中有如下代码 , 其是锁降级的一个应用示例 。
class CachedData {Object data;volatile boolean cacheValid;final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();void processCachedData() {// 获取读锁rwl.readLock().lock();if (!cacheValid) {// Must release read lock before acquiring write lock , 不释放的话下面写锁会获取不成功 , 造成死锁rwl.readLock().unlock();// 获取写锁rwl.writeLock().lock();try {// Recheck state because another thread might have// acquired write lock and changed state before we did.if (!cacheValid) {data = http://kandian.youth.cn/index/...cacheValid = true;}// Downgrade by acquiring read lock before releasing write lock// 这里再次获取读锁 , 如果不获取那么当写锁释放后可能其他写线程再次获得写锁 , 导致下方`use(data)`时出现不一致的现象// 这个操作就是降级rwl.readLock().lock();} finally {rwl.writeLock().unlock(); // Unlock write, still hold read}}try {// 使用完后释放读锁use(data);} finally {rwl.readLock().unlock();}} }}