Is it really necessary to lock.unlock()
in a finally
block? The following is a common idiom
try {
lock.lock();
// some code
} catch (Exception e) {
// exception handling
} finally {
lock.unlock();
}
however, here's what the Java Language Specification has to say:
If execution of the body is ever completed, either normally or abruptly [presumably, e.g. due to an exception], an unlock action is automatically performed on that same monitor.
Is it really necessary to put lock.unlock()
in a finally
block then? Or did I misunderstand the specification?
The section of the Java Language Specification (JLS) you linked is talking about synchronized
. Specifically, it's saying that if you have the following:
synchronized (someObj) {
// guarded code
}
Then when that synchronized
block exits for any reason, including an exception being thrown from within the block, it's guaranteed the implicit monitor of someObj
will be unlocked.
The java.util.concurrent.locks
API is a separate thing, and is not covered by the JLS (though obviously relies on the Java Memory Model (JMM) as defined by the JLS). That means you have to read the Javadoc of the API to know how its defined to work.
Here's an excerpt from the package documentation:
Interfaces and classes providing a framework for locking and waiting for conditions that is distinct from built-in synchronization and monitors [emphasis added]. The framework permits much greater flexibility in the use of locks and conditions, at the expense of more awkward syntax.
And here's an excerpt from the documentation of Lock
:
With this increased flexibility comes additional responsibility. The absence of block-structured locking removes the automatic release of locks that occurs with
synchronized
methods and statements [emphasis added]. In most cases, the following idiom should be used:Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }
As you can see, it's necessary to unlock the lock in a finally
block1. Also note that the call to lock()
is done before the try
block. This prevents trying to unlock the lock in the case where the call to lock()
failed for whatever reason.
1. A major reason why the java.util.concurrent.locks
API was created is to provide more flexibility than the block-structure of synchronized
. You may have a more complex scenario where you need to call unlock()
far removed from the call to lock()
or at some unspecified moment in the future, meaning calling unlock()
in a finally
block may not be the correct approach. But as noted by the documentation, that means you, the developer, have more responsibility. It's up to you to guarantee unlock()
is called when appropriate.