7

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

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可以接受吗?

4

3 回答 3

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();
    }
于 2013-05-05T07:07:48.630 回答
5

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

另外,在操作两个锁时要非常小心,因为如果一个线程在foothen上锁bar,而另一个线程在barthen上锁,则很有可能发生死锁foo

于 2013-05-05T07:09:34.997 回答
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();
    }
于 2013-05-05T07:18:33.800 回答