首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在同一个try块中锁定多个ReentrantReadWriteLocks是否安全?

在同一个try块中锁定多个ReentrantReadWriteLocks是否安全?
EN

Stack Overflow用户
提问于 2013-05-05 15:02:10
回答 3查看 944关注 0票数 7

假设我有两个关键资源,foo和bar。我用一些ReentrantReadWriteLock保护他们

代码语言:javascript
复制
ReentrantReadWriteLock foo = new RRWL() ...
ReentrantReadWriteLock bar = new RRWL() ...

大多数操作只使用foo或bar,但其中一些恰好两者都使用。现在,当使用单个锁时,您不能这样做:

代码语言:javascript
复制
void foo() {
   foo.writeLock().lock();
   privateWorkOnFoo();
   foo.writeLock().unlock();
}

如果抛出一个异常,你的foo将永远被锁定。相反,你把它包装起来,就像

代码语言:javascript
复制
void foo() {
    try {
        foo.writeLock().lock();
        privateWorkOnFoo();
    } finally { foo.writeLock().unlock(); }
}

但是,如果我需要同时处理这两个问题呢?把它们放在一个区块里安全吗?

选项1

代码语言:javascript
复制
try {
    foo.writeLock().lock();
    bar.writeLock().lock();
    magic();
} finally { 
    bar.writeLock().unlock();
    foo.writeLock().unlock();
}

或者,是否有必要为每个锁提供自己的块:

选项2

代码语言:javascript
复制
try {
    foo.writeLock().lock();
    try {
        bar.writeLock().lock();
        magic();
    } finally { 
      bar.writeLock().unlock();
    }
    
} finally { 
    foo.writeLock().unlock();
}

我不可能是第一个有困难调查此事的人...我知道选项2是“防弹的”,但它也需要大量的维护。选项1可以接受吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-05-05 15:07:49

选项1很好。它被称为双锁变体。如果您查看LinkedBlockingQueue操作,例如remove,它将锁定putLock和takeLock。以下是JDK的功能示例:

代码语言:javascript
复制
  public boolean remove(Object o) {
       if (o == null) return false;
       fullyLock();
       try
       {
       // ...
       }   
       finally {
         fullyUnlock();
       }
    }

   /**
     * Lock to prevent both puts and takes.
     */
    void fullyLock() {
        putLock.lock();
        takeLock.lock();
    }

    /**
     * Unlock to allow both puts and takes.
     */
    void fullyUnlock() {
        takeLock.unlock();
        putLock.unlock();
    }
票数 7
EN

Stack Overflow用户

发布于 2013-05-05 15:09:35

选项1实际上比选项2更安全,因为如果在选项2中抛出异常,第二个锁(foo)将不会被解锁:解锁不在finally块中。

此外,在操作两个锁时要非常小心,因为如果一个线程先锁定foo,然后锁定bar,而另一个线程先锁定bar,然后再锁定foo,则很有可能发生死锁。

票数 5
EN

Stack Overflow用户

发布于 2013-05-05 15:18:34

根据Lock API,lock()和unlock()方法都可能抛出异常。因此版本1是不正确的,因为第二次解锁可能永远不会被调用。版本2也是不正确的,您不应该在try块中调用lock(),因为如果lock.lock()抛出异常,则它没有被锁定,并且您不应该尝试解锁它。

正确的版本应该是这样的

代码语言:javascript
复制
    foo.writeLock().lock();
    try {
        bar.writeLock().lock();
        try {
            magic();
        } finally {
            bar.writeLock().unlock();
        }
    } finally { 
        foo.writeLock().unlock();
    }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16382193

复制
相关文章

相似问题

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