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基准测试,其中一个类似的锁几乎总是失败的

如果您使用Java 8,您可能想看看
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();
    }
}