将多个ReentrantReadWriteLock锁定在同一个try块中是否安全?

时间:2013-05-05 07:02:10

标签: java concurrency reentrantreadwritelock

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

ReentrantReadWriteLock foo = new RRWL() ...
ReentrantReadWriteLock bar = new RRWL() ...

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

void foo() {
   foo.writeLock().lock();
   privateWorkOnFoo();
   foo.writeLock().unlock();
}

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

void foo() {
    try {
        foo.writeLock().lock();
        privateWorkOnFoo();
    } finally { foo.writeLock().unlock(); }
}

但如果我需要兼顾两者呢?将它们放在一个区块中是否安全?

选项1

try {
    foo.writeLock().lock();
    bar.writeLock().lock();
    magic();
} finally { 
    bar.writeLock().unlock();
    foo.writeLock().unlock();
}

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

选项2

try {
    foo.writeLock().lock();
    try {
        bar.writeLock().lock();
        magic();
    } finally { 
      bar.writeLock().unlock();
    }

} finally { 
    foo.writeLock().unlock();
}

我不可能是第一个在此之前难以调查的人...我知道选项2有“防弹”但它也是一个相当多的维护。选项1是否可以接受?

3 个答案:

答案 0 :(得分:7)

选项1没问题。它被称为两种锁定变体。如果你看一下LinkedBlockingQueue操作,比如remove,它会锁定putLock和takeLock。以下是JDK的作用示例:

  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();
    }

答案 1 :(得分:5)

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

另外,在操作两个锁时要非常小心,因为如果一个线程锁定foo然后bar,并且另一个线程锁定bar,那么foo很可能会出现死锁}。

答案 2 :(得分:0)

根据Lock API,lock()和unlock()方法都可能抛出异常。因此版本1不正确,因为可能永远不会调用第二个解锁。版本2也不正确,你不应该在try块中调用lock(),因为如果lock.lock()抛出异常,那么它就没有被锁定,你不应该试图解锁它。

正确的版本应该是这样的

    foo.writeLock().lock();
    try {
        bar.writeLock().lock();
        try {
            magic();
        } finally {
            bar.writeLock().unlock();
        }
    } finally { 
        foo.writeLock().unlock();
    }