Java 为什么不是';是否允许读写锁升级?
Java 为什么不是';是否允许读写锁升级?,java,concurrency,reentrantlock,reentrantreadwritelock,Java,Concurrency,Reentrantlock,Reentrantreadwritelock,ReadWriteLock降级是由ReentrantReadWriteLock实现允许的(tryLock()下面的示例总是返回true): 不允许以类似方式升级锁的背后是什么想法?下面用于写锁的tryLock()方法可以安全地返回truew/o死锁风险,因为没有其他线程持有读锁: 首先,让我们注意到,就ReadWriteLocks的语义复杂性而言,升级和降级并不等同 您不需要避开争用来完成降级事务,因为您已经拥有锁上升级次数最多的权限,并且保证您是当前执行降级的唯一线程。升级情况并非如此,因此支
ReadWriteLock
降级是由ReentrantReadWriteLock
实现允许的(tryLock()
下面的示例总是返回true
):
不允许以类似方式升级锁的背后是什么想法?下面用于写锁的tryLock()
方法可以安全地返回true
w/o死锁风险,因为没有其他线程持有读锁:
首先,让我们注意到,就
ReadWriteLock
s的语义复杂性而言,升级和降级并不等同
您不需要避开争用来完成降级事务,因为您已经拥有锁上升级次数最多的权限,并且保证您是当前执行降级的唯一线程。升级情况并非如此,因此支持升级的机制自然需要更加复杂(或更加智能)
为了可用,升级机制需要在两个读取线程同时尝试升级时防止死锁(或者特别针对ReentrantReadWriteLock
,在持有多个读取锁的单个读取线程尝试升级时)。此外,该机制还需要指定如何处理失败的升级请求(其读锁是否会失效),这一点甚至不那么重要
正如您现在可能看到的,在ReentrantReadWriteLock
中完全处理这些问题至少是不方便的(尽管如此,.NET还是尝试了这一点,我认为实际上成功了)。我的猜测是,虽然final boolean writeLockAcquired=readWriteLock.writeLock().tryLock()
本可以在一些琐碎的情况下获得成功,但升级能力仍然不够好,无法用于普通用途-在足够激烈的争用下,如果您失去了对写锁的争夺,您将处于同一条船上,就像您解锁了读锁并试图获得写锁一样(给其他人留下潜入的机会,并在两者之间设置写锁)
提供锁升级功能的一个很好的方法是只允许一个线程尝试升级-这就是.NET所做的或所做的。但是我仍然建议Java 8的StampedLock
作为:
- 在低争用情况下,它的乐观读取比使用读取锁快得多
- 它的API对升级的限制要小得多(从乐观读取到读锁再到写锁)
- 我尝试创建一个现实的JMH基准测试,其中一个类似的锁几乎总是失败的
StampedLock
。更一般地说,您可能想看看Angelika Langer的作品。很有意思吧?;)
void downgrade(final ReadWriteLock readWriteLock) {
boolean downgraded = false;
readWriteLock.writeLock().lock();
try {
// Always true, as we already hold a W lock.
final boolean readLockAcquired = readWriteLock.readLock().tryLock();
if (readLockAcquired) {
// Now holding both a R and a W lock.
assert ((ReentrantReadWriteLock) readWriteLock).getReadHoldCount() == 1;
assert ((ReentrantReadWriteLock) readWriteLock).getWriteHoldCount() == 1;
readWriteLock.writeLock().unlock();
downgraded = true;
try {
// Now do some work with only a R lock held
} finally {
readWriteLock.readLock().unlock();
assert ((ReentrantReadWriteLock) readWriteLock).getReadHoldCount() == 0;
assert ((ReentrantReadWriteLock) readWriteLock).getWriteHoldCount() == 0;
}
}
} finally {
if (!downgraded) {
// Never (we were holding a W lock while trying a R lock).
readWriteLock.writeLock().unlock();
}
assert ((ReentrantReadWriteLock) readWriteLock).getReadHoldCount() == 0;
assert ((ReentrantReadWriteLock) readWriteLock).getWriteHoldCount() == 0;
}
}
void upgrade(final ReadWriteLock readWriteLock) {
readWriteLock.readLock().lock();
try {
// Always false: lock upgrade is not allowed
final boolean writeLockAcquired = readWriteLock.writeLock().tryLock();
// ...
} finally {
readWriteLock.readLock().unlock();
}
}