真的有必要将 lock.unlock() 放在 finally 块中吗?

Jor*_*Joh 0 java multithreading thread-synchronization

真的有必要lock.unlock()在一个finally街区吗?下面是一个常见的习语

try {
    lock.lock();
    // some code
} catch (Exception e) {
    // exception handling
} finally {
    lock.unlock();
}
Run Code Online (Sandbox Code Playgroud)

然而,Java 语言规范是这样说的:

如果主体的执行曾经完成,无论是正常还是突然 [大概,例如由于异常]都会在同一监视器上自动执行解锁操作。

那么真的有必要加lock.unlock()块吗?finally或者我误解了规范?

Sla*_*law 8

您链接的Java 语言规范(JLS)部分正在讨论synchronized. 具体来说,它是说如果您有以下情况:

synchronized (someObj) {
    // guarded code
}
Run Code Online (Sandbox Code Playgroud)

然后,当该synchronized块因任何原因退出时,包括从块内抛出异常,都可以保证隐式监视器someObj将被解锁。

APIjava.util.concurrent.locks是一个单独的东西,JLS 没有涵盖(尽管显然依赖于 JLS 定义的 Java 内存模型 (JMM))。这意味着您必须阅读 API 的 Javadoc 才能了解其定义如何工作。

以下是软件包文档的摘录:

接口和类提供了锁定和等待条件的框架,这与内置同步和监视器不同 [强调]。该框架在使用锁和条件方面提供了更大的灵活性,但代价是语法更加尴尬。

这是文档的摘录Lock

随着灵活性的增加,责任也随之增加。块结构锁定的缺失消除了synchronized方法和语句中发生的锁的自动释放 [强调]。在大多数情况下,应该使用以下习惯用法:

Lock l = ...;
l.lock();
try {
  // access the resource protected by this lock
} finally {
  l.unlock();
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,需要解锁finally1中的锁。另请注意,对的调用lock()是在块之前try完成的。这可以防止在调用因lock()任何原因失败的情况下尝试解锁锁。


1. 创建 API 的一个主要原因java.util.concurrent.locks是为了提供比synchronized. 您可能有一个更复杂的场景,您需要在unlock()远离调用的地方lock()或在未来某个未指定的时刻进行调用,这意味着unlock()finally块中调用可能不是正确的方法。但正如文档所指出的,这意味着您(开发人员)有更多的责任。由来保证unlock()在适当的时候被调用。

  • 在正常操作中,是的,如果线程不能立即获取锁,它就会阻塞。但是 [`Lock#lock()`](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/locks/Lock.html#lock ()) 被定义为让实现抛出未经检查的异常,这意味着调用可能会在不获取锁的情况下失败(即使这不太可能)。还有 [`Lock#lockInterruptically()`](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/locks/Lock.html# lockInterruptically()),您可能想要使用它,并且它可以抛出“InterruptedException”。 (2认同)