JDK 源码阅读011:ReentrantReadWriteLock
1 引言
读写锁,可以加读锁,也可以加写锁。
读锁和写锁是互斥的,也就是说,你加了读锁之后,就不能加写锁;如果加了写锁,就不能加读锁。但是如果有人加了读锁之后,别人可以同时加读锁。
如果你有一份数据,有人读,有人写,如果你全部都是用synchronized的话,会导致如果多个人读,也是要串行化,一个接一个的读。我们希望的效果是多个人可以同时来读,如果使用读锁和写锁分开的方式,就可以让多个人来读数据,多个人可以同时加读锁。
小结:
- 如果有人在读数据,就不能有人写数据,读锁 -> 写锁 -> 互斥
- 如果有人在写数据,别人不能写数据,写锁 -> 写锁 -> 互斥;如果有人在写数据,别人也不能读数据,写锁 -> 读锁 > 互斥
2 写锁实现原理
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
Thread current = Thread.currentThread();
// 获取到一个state = 0
int c = getState();
// // w,剧透一下,人家读写锁,非常聪明的利用state的值
// 二进制值里面的高低16位分别代表了读锁和写锁,AQS就一个,state
// state二进制值的高16位代表了读锁,低16位代表了写锁
// 可以认为下面的w就是从c(二进制值)通过位运算
// 获取到了state的低16位,代表了写锁的状态
int w = exclusiveCount(c);
// 如果c != 0,说明有人加过锁,但是此时c = 0
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);
return true;
}
// 非公平锁,此时一定会去尝试加锁
// 如果是公平锁,此时会判断如果队列中有等待线程,就不加锁
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
static final int SHARED_SHIFT = 16;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
3 可重入写锁实现原理
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
// 获取到一个state = 1
int c = getState();
int w = exclusiveCount(c);
// 如果c != 0,说明有人加过锁,但是此时c = 0
if (c != 0) {
// c != 0,w == 0,c肯定不是0,但是低16位是0,说明有人加了读锁,没有人加写锁,
// 此时你要加写锁,而且你还不是之前加锁的那个线程,加锁失败
if (w == 0 || current != getExclusiveOwnerThread())
return false;
// 走到这里说明:加了写锁且之前是当前线程加的锁
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// 加锁成功,设置值
setState(c + acquires);
return true;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
4 读写锁的护互斥实现原理
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
* 1. If write lock held by another thread, fail.
* 2. Otherwise, this thread is eligible for
* lock wrt state, so ask if it should block
* because of queue policy. If not, try
* to grant by CASing state and updating count.
* Note that step does not check for reentrant
* acquires, which is postponed to full version
* to avoid having to check hold count in
* the more typical non-reentrant case.
* 3. If step 2 fails either because thread
* apparently not eligible or CAS fails or count
* saturated, chain to version with full retry loop.
*/
Thread current = Thread.currentThread();
int c = getState();
// 已经被别的线程加写锁,加锁失败
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);
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 != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}