ReentrantReadWriteLock之初识读锁加锁流程
读锁加锁的初步流程:
- 获取state值
- 如果有线程持有写锁,并且持有写锁的不是当前线程,进入同步队列中排队
- 如果没有线程持有锁,或者持有写锁的是当前线程,竞争读锁
- 如果竞争的是非公平锁,如果同步队列中没有节点排队,竞争读锁
- 如果有节点排队,但head的next是读线程,当前线程依然可以竞争读锁
- 如果head的next是写线程,则需要继续判断当前线程是否是锁重入
- 如果是,执行锁重入过程,如果不是,进入同步队列中排队
- 如果竞争的是公平锁,如果同步队列中没有节点在排队,竞争读锁,
- 如果有,则需要继续判断当前线程是否是锁重入
- 如果是,执行锁重入过程,如果不是,进入同步队列中排队
public void lock() {
sync.acquireShared(1);
}
public final void acquireShared(int arg) {
// 判断是否获取读锁成功
// 如果tryAcquireShared(arg) < 0 说明获取失败,需要放入同步队列中
// 否则获取成功,直接返回
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
// 获取state值
int c = getState();
// 判断是否有线程持有写锁,判断是否是当前线程持有写锁
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
// 不是当前线程持有写锁
return -1;
// 没有线程持有写锁,或者当前线程持有写锁
// 获取state高16位值
int r = sharedCount(c);
// 竞争公平锁:查看同步队列中是否有线程排队,如果有不进入if中,直接去执行fullTryAcquireShared方法
// 竞争非公平锁:查看同步队列中是否有线程排队,没有就抢锁资源;
// 有排队的,但是head的next是读线程,那么就继续抢锁资源
// 如果是写线程,不进入if中,直接去执行fullTryAcquireShared方法
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
// 此处省略代码...
return 1;
}
// 抢锁失败,继续抢锁
return fullTryAcquireShared(current);
}
readerShouldBlock方法在公平锁和非公平锁下有不同的实现
非公平锁:
final boolean readerShouldBlock() {
// apparentlyFirstQueuedIsExclusive方法
// 当同步队列中有写线程节点时,返回true
// 当同步队列中没有写线程节点时,返回false
return apparentlyFirstQueuedIsExclusive();
}
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
公平锁:
final boolean readerShouldBlock() {
// hasQueuedPredecessors方法
// 如果同步队列中有节点,返回true
// 如果同步队列中没有节点,返回false
return hasQueuedPredecessors();
}
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}