公平性
非公平模式
以非公平模式进行初始化时,锁的获取的顺序是不确定的。非公平模式竞争性更强,有些在同步队列中等待的线程可能延迟较多,但是会比公平锁有更高的吞吐量。
公平模式
当以公平模式初始化时,线程将会以队列的顺序获取锁。
重入性
读写锁允许读线程和写线程按照请求锁的顺序重新进入抢占读锁和写锁。只有写线程释放了锁,读线程才能获取重入锁。
写线程获取写锁后可以再次获取读取锁,但是读线程获取读取锁后却不能获取写入锁。
重入数
读写锁最多支持65535个递归写入锁。
最多支持65535个递归读取锁。
锁降级
写线程在抢占写锁后可以抢占读锁,如果再释放写锁,这样就从写锁变成了读锁,从而实现锁降级的特性。
ReetrantReadWriteLock类源码解析
1,可重入次数计算
上一章,我们讲到过,可重入锁的可重入次数是AQS的state字段表示,那么,这里既有读锁,又有写锁,如何记录可重入次数呢?
如下代码所示:
sharedCount的值是c的高16位。
exclusiveCount的值是c的低16位,
c就是原AQS类中的state。
就是说,原state被分为高16位和低16位,分别表示读锁的重入次数和写锁的重入次数,最大为2的16次-1,65535次。
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1
static final int MAX_COUNT = (1
static final int EXCLUSIVE_MASK = (1
/** Returns the number of shared holds represented in count */
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
/** Returns the number of exclusive holds represented in count */
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
2,共享锁(读锁)的获取
步骤1,判断是有独占锁(写锁),且占有线程非当前线程,也返回失败。
步骤2,判断当前是否需要阻塞,且重入次数未超限,则开始进入抢占锁。注意重入次数以SHARED_UNIT为单位,因为使用state的高16位作为重入次数。
是否需要阻塞,在非公平锁中,实现如下,如果AQS的锁等待队列head节点后的节点是非共享的节点(就是不在等待读锁),将返回true。
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);
if (!readerShouldBlock() &&
r
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);
}
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
3,共享锁(读锁)的释放
步骤1,释放当前线程的持有锁数量
步骤2,尝试使用CAS方式释放当前读锁。
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count
readHolds.remove();
if (count
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
// Releasing the read lock has no effect on readers,
// but it may allow waiting writers to proceed if
// both read and write locks are now free.
return nextc == 0;
}
}
至于写锁的处理,请看上两章的内容。
Concurrent包
感兴趣的小伙伴请关注本人公众号:暖爸的java家园
领取专属 10元无门槛券
私享最新 技术干货