JUC 代码浅析 [3] ——基于 AQS 的锁 ReentrantReadWriteLock
ReentrantReadWriteLock 也是基于 AQS 实现的锁,它的特点是一个资源能够被多个读线程访问,或者被一个写线程访问,读和写是互斥的,可以同时有多个读但只能有一个写,大量读操作的场景下性能较好。 ReentrantReadWriteLock 并没有实现 Lock 接口,而是内部实现了 ReadLock 和 WriteLock 分别针对读和写操作。看起来像两个锁,但其实这两个锁只是进行了一层适配,它们的主体都是 Sync , ReadLock 和 WriteLock 分别使用 Sync 的共享和独占模式。
ReadLock 加锁和释放,就是向 sync 请求共享模式进入和释放
public void lock () {
sync.acquireShared(1);
}
public void unlock () {
sync.releaseShared(1);
}
WriteLock 加锁和释放,就是向 sync 请求互斥模式进入和释放
public void lock() {
sync.acquire (1);
}
public void unlock () {
sync.release(1);
}
sync 的实现有公平和非公平两种 ( 下面介绍区别 ) , sync 使用 int 的低 16 位表示写计数,高 16 位表示读计数,所以读锁和写锁的最高重入次数为 65535 ,超出将抛出 Error 。
获取写锁的实现片段,逻辑是:
l 如果读计数不为 0 ,失败;
l 如果写计数不为 0 并且不是当前线程持有锁,失败
l 如果写计数超出 65535 ,失败,抛出 Error
l 如果写计数为 0 并且当前线程需要阻塞,或者 state 被其他线程改变了,失败
l 以上条件都不满足则当前线程拥有写锁
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” );
}
if ((w == 0 && writerShouldBlock(current)) ||
!compareAndSetState(c, c + acquires))
return false ;
setExclusiveOwnerThread(current);
return true ;
}
非公平 sync 的写线程在总是不需要阻塞
final boolean writerShouldBlock (Thread current) {
return false ; // writers can always barge
}
公平 sync 的写线程只要 AQS 队列不为空并且不在第一个位置的就需要阻塞
final boolean writerShouldBlock(Thread current) {
// only proceed if queue is empty or current thread at head
return !isFirst(current);
}
写锁的释放,减去并检查 state 计数,为 0 则表示写锁已经可以释放了,不为 0 说明该线程重入了写锁并把剩余的计数写入 state
protected final boolean tryRelease ( int releases) {
int nextc = getState() – releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
if (exclusiveCount(nextc) == 0) {
setExclusiveOwnerThread( null );
setState(nextc);
return true ;
} else {
setState(nextc);
return false ;
}
}
读锁的获取,
1 如果已经有线程获得写锁,并且不是当前线程获得的,失败
2 如果重入的读计数超出 65535 ,失败,抛出 Error
3 否则如果线程不用阻塞,并且增加读计数成功,则成功获得读锁
4 如果第三步失败(可能由于线程需要阻塞或者 CAS 修改失败), 不断尝试去修改状态直到成功或者锁被写入线程占有(看 fullTryAcquireShared 的实现 )
protected final int tryAcquireShared ( int unused) {
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
if (sharedCount(c) == MAX_COUNT)
throw new Error( “Maximum lock count exceeded” );
if (!readerShouldBlock(current) &&
compareAndSetState(c, c + SHARED_UNIT)) {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get();
rh.count++;
return 1;
}
return fullTryAcquireShared(current);
}
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
rh = readHolds.get();
for (;;) {
int c = getState();
int w = exclusiveCount(c);
if ((w != 0 && getExclusiveOwnerThread() != current) ||
((rh.count | w) == 0 && readerShouldBlock(current)))
return -1;
if (sharedCount(c) == MAX_COUNT)
throw new Error( “Maximum lock count exceeded”