读写锁是指对一个线程想实现读写分离, 但在对于多线程读操作希望实现共享能力.
- 一开始读不加锁, 多线程可以同时获取读’ 锁’ 能力, 会累计 state
- 写锁加锁是要求不包含任何读锁否则阻塞等待
- 写锁可重入
- 读锁加锁若发现当前处理线程不是自己则直接阻塞,若发现当前处理线程是自己则根据公平锁、非公平锁机制处理,若使用非公平锁则直接获取锁操作返回,若是使用非公平锁则给等待线程一个抢占锁的机会。
- 写锁也同样分公平/非公平锁, 对于非公平锁而言,会直接获取抢占修改线锁执行线程为当前线程,对于公平锁而言会给别人一个抢占的机会。
写锁
写锁加锁相对简单,因此从这里优先提及, 这里主要是为了理解公平锁、非公平锁在读写锁的用处。
protected final boolean tryAcquire(int acquires) {
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)
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;
}
abstract boolean writerShouldBlock();
static final class FairSync extends Sync {
private static final long serialVersionUID = -2274990926593161451L;
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
}
/* 自己是否也在阻塞中,且排在第一位 */
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
// 自己是否已经阻塞在第一位 ( 很奇怪,只有自己才能阻塞自己 )
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -8159625535654395037L;
final boolean writerShouldBlock() {
return false;
}
final boolean readerShouldBlock() {
// 判断等待队列第一个是否是独占锁
return apparentlyFirstQueuedIsExclusive();
}
}
读锁
读锁对公平锁、非公平锁的支持设计太巧妙了,我以一下方式尝试理解
- 线程 A 读
- 线程 A 写 ( 写锁加锁是因为不能有读锁因此阻塞 )
- 线程 B 读 ( 读加锁 )
- 线程 A 读 ( 这里可以走公平锁/非公平锁 )
/* aqs */
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);
// readerShouldBlock 是否加锁主要是为了在加读锁的时候给其他竞争锁一个抢夺锁的机会,对于非公平锁而言会直接获取到该锁,对于公平锁而言会尝试去获取锁
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);
}