ReadWriteLock维护一对关联的locks ,一个用于只读操作,一个用于写入。只要没有写者, read lock可能被多个read lock线程同时持有。 write lock是独占的。所有ReadWriteLock实现必须保证writeLock操作的内存同步效果(如Lock接口中指定的那样)相对于关联的readLock也成立。 也就是说,成功获取读锁的线程将看到在先前释放写锁时所做的所有更新。public interface ReadWriteLock {/*** Returns the lock used for reading.** @return the lock used for reading*/Lock readLock();/*** Returns the lock used for writing.** @return the lock used for writing*/Lock writeLock();}
共享模式获取读锁,请注意,该步骤不检查可重入
protected final int tryAcquireShared(int unused) {/** 演练:* 1. 如果写锁被另一个线程持有,则失败。* 2. 否则,该线程有资格* lock wrt state,所以询问它是否应该阻塞* 因为队列策略。如果没有,请尝试* 通过 CASing 状态和更新计数来授予。* 请注意,该步骤不检查可重入* 获取,它被推迟到完整版本* 以避免在* 更典型的不可重入情况下检查保持计数。* 3. 如果步骤 2 因线程* 显然不符合条件或 CAS 失败或计数* 饱和而失败,则链接到具有完整重试循环的版本。*/Thread current = Thread.currentThread();int c = getState();##获取独占的持有数,如果不为0,说明当前有写线程并且本线程不是写线程,那么失败if (exclusiveCount(c) != 0 &&getExclusiveOwnerThread() != current)return -1;##获取读锁的个数int r = sharedCount(c);##如果读不应该阻塞(readerShouldBlock返回false)并且读锁的个数小于最大值65535,## 并且可以成功更新状态值,成功if (!readerShouldBlock() &&r < MAX_COUNT &&## c更新成?+65536,即 65536的二进制就是0的表示;那就是高16位+1;compareAndSetState(c, c + SHARED_UNIT)) {##如果当前读锁为0if (r == 0) {##第一个读线程就是当前线程firstReader = current;firstReaderHoldCount = 1;##如果当前线程重入了,记录firstReaderHoldCount} 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++;}return 1;}##否则,循环尝试return fullTryAcquireShared(current);}
/** 读取与写入计数提取常量和函数。* 锁状态在逻辑上分为两个无符号短:* 较低的表示独占(写入器)锁定保持计数,* 和上部表示共享(读取器)保持计数。*/static final int SHARED_SHIFT = 16;static final int SHARED_UNIT = (1 << SHARED_SHIFT);static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;/** 返回以计数表示的共享保留数 */static int sharedCount(int c) {// c 也就是state 无符号右移16位,返回的高16位,也就是当前读锁的计数。return c >>> SHARED_SHIFT;}/** 返回以 count 表示的独占持有数 */static int exclusiveCount(int c) {//首先 EXCLUSIVE_MASK = 1 << 16 - 1; 即 1乘以2的16次方 -1 = 65535;##写为二进制就是“1111111111111111”(16个“1”)//如果c 即getState() 返回不等于0,但是只有0 & 低16位返回 0才是无独占。return c & EXCLUSIVE_MASK;}
循环重试
/*** 完整版的读取获取,处理 CAS 未命中* 和在 tryAcquireShared 中未处理的重入读取。*/final int fullTryAcquireShared(Thread current) {/* * 此代码与* tryAcquireShared 中的代码部分冗余,但总体上更简单,因为* 重试和延迟读取保持计数之间的交互不会使 tryAcquireShared 复杂化。*/HoldCounter rh = null;for (;;) {int c = getState();##一旦有别的线程获得了写锁,返回-1,失败if (exclusiveCount(c) != 0) {if (getExclusiveOwnerThread() != current)return -1;// else we hold the exclusive lock; blocking here// would cause deadlock.##如果读线程需要阻塞} else if (readerShouldBlock()) {// Make sure we're not acquiring read lock reentrantlyif (firstReader == current) {// assert firstReaderHoldCount > 0;} 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;}}}
独占模式获取写锁
protected final boolean tryAcquire(int acquires) {/* * 演练:* 1. 如果读取计数非零或写入计数非零* 并且所有者是不同的线程,则失败。* 2. 如果计数饱和,则失败。 (这只有在 count 已经非零时才会发生。)* 3. 否则,如果 * 它是可重入获取或* 队列策略允许,则此线程有资格获得锁定。如果是这样,更新状态* 并设置所有者。*/##得到调用lock方法的当前线程Thread current = Thread.currentThread();int c = getState();##获取独占锁的持有数int w = exclusiveCount(c);##如果当前有写锁或者读锁if (c != 0) {// (Note: if c != 0 and w == 0 then shared count != 0)##如果写锁为0或者当前线程不是独占线程(不符合重入),返回falseif (w == 0 || current != getExclusiveOwnerThread())return false;##如果写锁的个数超过了最大值,抛出异常if (w + exclusiveCount(acquires) > MAX_COUNT)throw new Error("Maximum lock count exceeded");// Reentrant acquire##写锁重入,返回truesetState(c + acquires);return true;}##如果当前没有写锁或者读锁(c!=0),如果写线程应该阻塞或者CAS失败,返回falseif (writerShouldBlock() ||!compareAndSetState(c, c + acquires))return false;##/否则将当前线程置为获得写锁的线程,返回truesetExclusiveOwnerThread(current);return true;}
读锁释放
释放锁的第一步是更新firstReader或HoldCounter的计数,接下来进入死循环,尝试更新AQS的状态,
一旦更新成功,则返回;否则,则重试。
protected final boolean tryReleaseShared(int unused) {Thread current = Thread.currentThread();if (firstReader == current) {// assert firstReaderHoldCount > 0;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;}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;}}
写锁释放
调用tryRelease尝试释放锁,一旦释放成功了,那么如果等待队列中有线程再等待,那么调用unparkSuccessor将下一个线程解除挂起。
public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h);return true;}return false;}protected final boolean tryRelease(int releases) {##如果没有线程持有写锁,但是仍要释放,抛出异常if (!isHeldExclusively())throw new IllegalMonitorStateException();int nextc = getState() - releases;boolean free = exclusiveCount(nextc) == 0;##如果没有写锁了,那么将AQS的线程置为nullif (free)setExclusiveOwnerThread(null);##AQS的状态总是要更新的setState(nextc);return free;}
小知识:
一般二进制“0”就表示0,你说的情况应该是存在有进位标志。
16位的二进制数能表示的最大数字为“2的16次方减1”,即65535,写为二进制就是“1111111111111111”(16个“1”)。
如果将65535再加1,就会发生进位,二进制就变成了“10000000000000000”(“1”后面16个“0”),这样就是17位二进制数了。但在只有16位二进制空间的时候,就成为“0000000000000000”(16个“0”)。如果没有进位标志,这个数就被理解为0(即65535+1=0)。如果存在进位标志,则当运算发生进位时,进位标志被置“1”,能够知道这16个“0”是进位后得到的,那么这个数就被理解为65536。
