ReentrantReadWriteLock
读写锁学习–未完
JDK1.8读写锁包含了两个锁,读锁readerLock
,写锁writerLock
读锁和写锁的的lock()都使用了同一个同步器Sync
,唯一区别的是一个是共享锁一个是排它锁
//读锁
public void lock() {
sync.acquireShared(1);
}
//写锁
public void lock() {
sync.acquire(1);
}
1. 读锁
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
if条件尝试获取读锁,调用tryAcquireShared(int unused)
//Sync
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
//获取当前锁的状态
int c = getState();
//exclusiveCount(c) 返回独占锁(写锁)持有锁的数量
//exclusiveCount(c)!=0 存在写锁而且持有锁的线程不是当前线程,返回-1
//进入doAcquireShared(arg),等会看
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//获取共享锁(读锁)持有的数量
int r = sharedCount(c);
/*
!readerShouldBlock() 读锁是否应该被堵塞,公平锁判断是否有前驱
非公平锁判断是否
r < MAX_COUNT共享线程数小于最大共享数
compareAndSetState(c, c + SHARED_UNIT) 尝试改变读锁的数量
*/
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
//如果之前没有人使用过锁(空闲),将当前线程设置为第一个读线程,读锁+1,直接上锁完事
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
//如果队列空闲,直接读锁+1(重入)
firstReaderHoldCount++;
} else {
//如果当前线程不是队列的第一个
//The hold count of the last thread to successfully acquire readLock
//取出最后一个成功获取读锁的线程
HoldCounter rh = cachedHoldCounter;
//如果cache没有或者最后获取读锁的不是当前线程,则取出当前线程的累计数
//设置当前线程为最后一个成功获取读锁的线程
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
//如果rh != null 且 累计数为0,将rh设置到ThreadLocal,
else if (rh.count == 0)
readHolds.set(rh);
//累计次数+1
rh.count++;
}
return 1;
}
//如果进行到这一步,条件可能是:
// 1.锁被其他线程占用
// 2.读线程累计数达到上限
return fullTryAcquireShared(current);
}
//Fair
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
//h != t,head!=tail,队列非空
// (s = h.next) == null 头的下一个节点为空,这情况应该不可能,既然队列非空了
// head肯定指向像一个节点,貌似就是一个赋值的过程(个人理解),jdk这帮人有点懒....
// s.thread != Thread.currentThread()说明在当前线程之前还有人等待获取锁
// 整个方法就是判断CLH队列有没有前驱节点在等待获取锁
return h != t &&
( (s = h.next) == null || s.thread != Thread.currentThread());
}
//Nonfair
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
//(h = head) != null 说明CLH已经初始化,顺便赋值给h
//(s = h.next) != null 说明队列非空,顺便赋值s
// !s.isShared() 判断下一个获取锁的线程是不是写线程
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
final boolean isShared() {
return nextWaiter == SHARED;
}
//非公平的情况下,判断了CLH下一个要获得锁的是不是写线程,如果是,那么读线程被堵塞,
//进入fullTryAcquireShared
final int fullTryAcquireShared(Thread current) {
/*
* This code is in part redundant with that in
* tryAcquireShared but is simpler overall by not
* complicating tryAcquireShared with interactions between
* retries and lazily reading hold counts.
*/
//这段代码在一定程度上和tryAcquireShared冗余,但是总体来说是简化了
//而不是使得tryAcquireShared在重试和延迟读取持有锁数量的过程复杂化
HoldCounter rh = null;
for (;;) {
//当前线程
int c = getState();
//存在写锁
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 {
//第一次循环,rh=null,赋值一下
if (rh == null) {
//将最后获取到读锁的线程的计数器赋值给rh
rh = cachedHoldCounter;
//如果rh还是为空(),或者最后获取到读锁的线程不是当前线程
if (rh == null || rh.tid != getThreadId(current)) {
//取出自己的持有锁计数器
rh = readHolds.get();
//如果自己的读锁计数器的计数为0
//将自己的读锁计数器移除
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
//如果读锁累计数达到最大了,抛异常
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
//尝试设置读锁状态+1
if (compareAndSetState(c, c + SHARED_UNIT)) {
//如果锁是空闲的
if (sharedCount(c) == 0) {
//设置当前线程为读锁的第一个线程
firstReader = current;
//设置读锁第一个读线程计数器计数设为1
firstReaderHoldCount = 1;
} else if (firstReader == current) {
//如果当前线程就是读锁中第一个读线程,则当前线程获取读锁累计数+1
firstReaderHoldCount++;
} else {
//如果当前线程不是读锁的第一个线程,且读锁不是空闲的
//第一次循环,rh=null的,赋值一下
if (rh == null)
rh = cachedHoldCounter;
//如果赋值之后,还是空的或者最后获取到读锁的线程并不是当前线程
if (rh == null || rh.tid != getThreadId(current))
//将自己的持有锁计数器取出来
rh = readHolds.get();
else if (rh.count == 0)
//如果自己持有锁计数器的累计数为0,初始化一下
readHolds.set(rh);
//最后再将自己持有读锁的计数器+1
rh.count++;
//将最后成功获取到读锁的计数器设置为当前
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
//fullTryAcquireShared 方法整体就是循环cas,将读锁状态设为+1