The accepted answer is true in nearly all aspects, but it is still only halve the truth at all (ok, 95% of the truth).
Assume the following code:
private final Lock m_Lock = new ReentrantLock();
…
public final SomeObject doSomething( final SomeObject arg )
{
final SomeObject retValue;
try
{
lock.lock();
retValue = SomeObject( arg );
}
finally
{
out.println( "Entering finally block");
callingAnotherMethod( arg, retValue );
lock.unlock();
}
return retValue;
}
…
try
{
final var result = doSomething( new SomeObject() );
}
catch( final StackOverflowError e ) { /* Deliberately ignored */ }
Calling the method doSomething() will cause a StackOverflowError nearly immediately.
And the lock will not be released!
But how could this happen when the finally block is always executed (with the exceptions already listed in the accepted answer)?
That is because there is no guarantee made that all statements in the finally block are really executed!
This would be obvious if there would be a call to System.exit() or a throws statement before the call to lock.unlock().
But there is nothing like that in the sample code …
Aside that the two other method calls in the finally block before the call to lock.unlock() will cause another StackOverflowError …
And voilà, the lock is not released!
Although the sample code as such is silly, similar patterns can be found a lot in many kinds of software. All works fine as long as nothing ugly happens in the finally block …
Funny fact is that it does not work in later versions of Java (meaning that in later versions, the lock was released …). No idea when and why this changed.
But you still have to make sure that the finally block always terminates normally, otherwise it might not matter if (that) it always get executed …