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)) {
##如果当前读锁为0
if (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 reentrantly
if (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或者当前线程不是独占线程(不符合重入),返回false
if (w == 0 || current != getExclusiveOwnerThread())
return false;
##如果写锁的个数超过了最大值,抛出异常
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
##写锁重入,返回true
setState(c + acquires);
return true;
}
##如果当前没有写锁或者读锁(c!=0),如果写线程应该阻塞或者CAS失败,返回false
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
##/否则将当前线程置为获得写锁的线程,返回true
setExclusiveOwnerThread(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;
else
firstReaderHoldCount--;
} 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的线程置为null
if (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。