当前位置: 首页 > 面试题库 >

Java ReentrantReadWriteLocks-如何安全获取写锁?

司寇山
2023-03-14
问题内容

我现在在代码中使用ReentrantReadWriteLock来同步树状结构的访问。这种结构很大,可以被许多线程同时读取,偶尔对其一部分进行修改,因此似乎很适合读写习惯。我了解到,对于这一特定类,不能将读取锁提升为写入锁,因此根据Javadocs,必须在获得写入锁之前释放读取锁。之前,我已经在非可重入上下文中成功使用了这种模式。

但是,我发现我无法可靠地获取写锁而不会永远阻塞。由于读锁是可重入的,而我实际上是这样使用的,因此简单的代码

lock.getReadLock().unlock();
lock.getWriteLock().lock()

如果我重新获得了readlock,则可以阻止。每次解锁请求只会减少保留计数,并且只有在保留计数达到零时才实际释放锁定。

编辑 以澄清这一点,因为我认为我一开始解释得不太好-
我知道此类中没有内置的锁升级,而且我必须简单地释放读取锁并获得写入锁。我的问题是/无论其他线程在做什么,如果重新获得了该调用,则调用getReadLock().unlock()实际上可能不会释放
线程的锁定,在这种情况下,getWriteLock().lock()由于该线程仍处于读取保持状态,因此对它的调用将永远阻塞锁定并因此阻止自身。

例如,即使在没有其他线程访问锁的情况下运行单线程,此代码片段也永远不会到达println语句:

final ReadWriteLock lock = new ReentrantReadWriteLock();
lock.getReadLock().lock();

// In real code we would go call other methods that end up calling back and
// thus locking again
lock.getReadLock().lock();

// Now we do some stuff and realise we need to write so try to escalate the
// lock as per the Javadocs and the above description
lock.getReadLock().unlock(); // Does not actually release the lock
lock.getWriteLock().lock();  // Blocks as some thread (this one!) holds read lock

System.out.println("Will never get here");

所以我问,有没有很好的成语来处理这种情况?具体而言,当持有读锁的线程(可能是可重入)发现其需要进行某些写操作,因此希望“挂起”自己的读锁以获取写锁(按其他线程的要求进行阻塞)释放对读锁的保持),然后在相同状态下“拾取”对读锁的保持?

由于此ReadWriteLock实现是专为可重入设计的,因此,当可以重新获得锁时,肯定有某种明智的方法可以将读锁提升为写锁?这是至关重要的部分,这意味着幼稚的方法行不通。


问题答案:

我在这方面取得了一些进展。通过显式地将lock变量声明为a
ReentrantReadWriteLock而不是简单地声明ReadWriteLock(不是理想值,但在这种情况下可能是必要的弊端),我可以调用该getReadHoldCount()方法。这使我可以获得当前线程的保持次数,因此可以多次释放该readlock(然后在以后重新获得相同的次数)。如此工作,如快速和肮脏的测试所示:

final int holdCount = lock.getReadHoldCount();
for (int i = 0; i < holdCount; i++) {
   lock.readLock().unlock();
}
lock.writeLock().lock();
try {
   // Perform modifications
} finally {
   // Downgrade by reacquiring read lock before releasing write lock
   for (int i = 0; i < holdCount; i++) {
      lock.readLock().lock();
   }
   lock.writeLock().unlock();
}

不过,这将是我能做的最好的事情吗?感觉不太优雅,我仍然希望有一种方法可以减少“手动”方式的处理。



 类似资料:
  • 问题内容: 让我们假设我们要还原以下字符串“áe”。 该代码为“ \ u0061 \ u0301 \ u0065”。 恢复它的幼稚方法是逐个字符 当我们希望获得“eá”(\ u0065 \ u0061 \ u0301)时,它会给我们“éa”(\ u0065 \ u0301 \ u0061)。重音“´”应与“ a”粘贴在一起,而不要更改为“ e”。 以下代码为我提供了该字符串的预期结果: 我正在检查

  • 问题内容: 假设您有一个用于处理某种XML文件或配置文件的库。该库将整个文件读入内存,并提供用于编辑内容的方法。处理完内容后,可以调用将内容保存回文件。问题是如何以安全的方式执行此操作。 覆盖现有文件(开始写入原始文件)显然是不安全的。如果该方法在完成之前失败,则最终将写入一半的文件,并且您丢失了数据。 更好的选择是在某个位置写入 临时 文件,方法完成后, 将 临时文件 复制 到原始文件。 现在,

  • X1.0新增 sp_get_favorite_key($table,$object_id) 功能: 用于生成收藏内容用的安全key,收藏时必用 参数: $table:收藏内容所在表,不带表前缀 $object_id:收藏内容的id 返回: 类型string,收藏内容用的安全key

  • 我有以下代码,在调用.getList()时抛出。get(0)为空。 如何编写安全的java 8可选代码(使用. map等),以防止. getList(). get(0)抛出?

  • 我需要帮助如何在我的服务器(index.js)中正确编写GET和POST请求,以及如何在应用程序中正确编写fetch。js。 我阅读了Stackoverflow上的线程,并搜索了有关如何编写请求和获取的信息,但我发现如何将示例添加到自己的代码中非常困难。我已经尝试了三个星期不同的解决方案,但都一事无成。所以,请帮忙。我觉得这应该没那么难,但出于某种原因确实如此。除了这里,我没有人需要帮助。 我正在