首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >检查“Reentrant读写锁”中的readLock拥有权

检查“Reentrant读写锁”中的readLock拥有权
EN

Stack Overflow用户
提问于 2016-12-13 17:54:00
回答 1查看 788关注 0票数 0

我在试图使用ReentrantReadWriteLock控制对具有许多不同读写操作的相当复杂的数据结构的访问时,出现了一些与此密切相关的问题。正如文档中的示例所示,我获取了一个读锁和一个写锁,并且我正在使用它们成功地管理对这个数据结构的并发访问。但是,在调试另一个问题时,我注意到有时在同一个线程中获得多个读锁。造成这种情况的根本原因是,我有许多复杂的查询调用更简单的查询(参见下面的示例)。复杂的查询可以看作是一个事务,即在getVersion()datamyQuery访问之间不应该有写操作。这个当前的解决方案可以工作,但这意味着在代码中的某些地方,我将拥有同一个线程拥有的多个读取锁。我已经注意到,写等价物有一个isHeldByCurrentThread()方法,但奇怪的是,在readLock中没有这种方法。我无法发现以下情况:

  • 在同一个线程中有多个读锁是不好的(风格差、性能差或者将来可能出错)吗?
  • 使用WriteLock.isHeldByCurrentThread检查错误(例如,期望写锁,但不存在,或者作为释放写锁的条件)是不好的吗?
  • 如果这是一个问题,是否有最佳做法来决定如何进行?是将lockedAquired布尔值传递给可能从已经拥有锁的代码中调用的方法,还是确保它们是唯一获得的,还是应该使用ReadLock.tryLock()

下面是代码示例:

代码语言:javascript
复制
public class GraphWorldModel {

  //unfair RW Lock (default, see javadoc)
  protected final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(false);
  protected final ReentrantReadWriteLock.ReadLock readLock = rwl.readLock();
  protected final ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock();

  protected long versionID = 0;
  protected Object data = null;

  @Override
  public long getVersion() {
      long result = -1;
      readLock.lock();
      try {
          result = this.versionID;
      } finally {

          readLock.unlock();
      }
      return result;
  }
  public ResultObject myQuery() {
      //do some work
      readLock.lock();
      try {
        long version = getVersion();
        ResultObject result = new ResultObject(this.data, version);
        //note: querying version and result should be atomic!
      } finally {
        readLock.unlock();
      }
      //do some more work
      return result;
  }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-12-13 23:53:07

  • 在同一个线程中有多个读锁是不好的(风格差、性能差或者将来可能出错)吗?

我想说这是值得怀疑的风格。首先,正如@JBNizet所指出的,你试图重新获得一个你已经拥有的锁是没有问题的。锁只将读取器计数增加一个,然后在最终解锁中将其递减。

但是,这确实意味着您必须跨越一个内存屏障(volatile读取)来更新共享锁统计信息,这意味着性能下降。这取决于执行此代码的频率,以及它是否会产生显著的性能差异。

然而,就big而言,有一个很大的问题。如果你说的只是只读锁,那么我不认为会有更多的错误发生。实际上,试图修复双锁问题(见下文)可能有更大的风险。但是在您的代码中,try/finally逻辑确保锁在完成时被适当地使用和解锁。

但是,,如果您讨论的是混合读锁和写锁,那么您需要理解以下代码死锁:

代码语言:javascript
复制
ReentrantReadWriteLock rrwl = new ReentrantReadWriteLock();
ReadLock readLock = rrwl.readLock();
WriteLock writeLock = rrwl.writeLock();
readLock.lock();
writeLock.lock();

或者,它至少会停止,直到其他一些代码解锁read-lock。如果要在代码中混合读锁和写锁,那么必须防止双重锁定。

  • 使用WriteLock.isHeldByCurrentThread检查错误(例如,期望写锁,但不存在,或者作为释放写锁的条件)是不好的吗?

这将为您的代码添加许多逻辑,这是更高的风险,但如果性能影响很大,这也许值得。尽管如此,另一种选择见下文。

  • 如果这是一个问题,是否有最佳做法来决定如何进行?是将lockedAquired布尔值传递给可能从已经拥有锁的代码中调用的方法,还是确保它们是唯一获得的,还是应该使用ReadLock.tryLock()?

我要做的是创建一个名为getVersionLocked()的内核方法。类似于:

代码语言:javascript
复制
@Override
public long getVersion() {
    readLock.lock();
    try {
        // NOTE: I refactored this to return out of the try/finally which is a fine pattern
        return getVersionLocked();
    } finally {
        readLock.unlock();
    }
}
public ResultObject myQuery() {
    //do some work
    readLock.lock();
    ResultObject result;
    try {
      long version = getVersionLocked();
      result = new ResultObject(this.data, version);
      //note: querying version and result should be atomic!
    } finally {
      readLock.unlock();
    }
    //do some more work
    return result;
}
// NOTE: to call this method, we must already be locked
private long getVersionLocked() {
    return this.versionID;
}

然后,您可以决定调用方法的锁定或解锁版本,并且只执行一次readLock()

这样做的风险更大,因为您可以在锁未持有时调用getVersionLocked(),但我认为这是一个可以接受的风险。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41127563

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档