Reputation: 6689
I'm wondering if given code like
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
is there any chance that executing thread can be somehow terminated after executing lock
method but before entering try-finally
block? That would result in a lock that is taken but never freed. Is there some line in Java/JVM specification that gives us any assurance that if the code is written using that idiom there is no chance of leaving locked-forever lock?
My question is inspired by the answer to C# related question Can Monitor.Enter throw an exception? that references two posts on MSDN
about problems with code like
Monitor.Enter(...)
try
{
...
}
finally
{
Monitor.Exit(..)
}
that in case of C# there is small chance that execution never reaches try-finally
based on machine code generated by JIT.
I'm aware that this may be seen as a contrived question/nitpicking but my curiosity got better of me.
Upvotes: 4
Views: 1327
Reputation: 572
Is there some line in Java/JVM specification that gives us any assurance that if the code is written using that idiom there is no chance of leaving locked-forever lock?
First of all i want to notice that in java there are structured and unstructured locks. Structured locks are that kind of locks where the locked code is encapsulated inside some structure (block) and is autoreleased in the end of this structure(block). There are as synchronized methods as synchronized blocks. In java you will get monitorenter (https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.monitorenter) instruction directly in bytecode only in case of using synchronized block. So if monitorenter instruction fails then this will be a fatal error which will cause jvm to stop. With syncrhonized method there is no monitorenter and monitorexit instructions directly in the the compile bytecode but that code in the syncrhonized block is marked for the jvm as synchronized and jvm will do that job by itself. So in this case if smth will go wrong then again this will be a fatal jvm error. So here the answer to your question is no because synchronized blocks or methods are compiled to native jvm instructions and their crashing will cause the whole jvm to crash.
Now let's talk about unstructered locks. These are locks where you have to take care about locking and unlocking by calling direct methods of that lock. Here you gain a lot of advantages of creating complex interesting constructions like chain locking and others. And again the answer to your question is no and actually it is absolutely possible for exception to be thrown in that method and it is also absolutely possible to get live or dead lock here. All this is possible due to the fact that unstructured locking is absolutely programmatic java concept. JVM knows nothing about unstructured locks. Lock in java is an interface (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html) and you can use OOB unstructered locks like Reentrant lock, Reentrant RW locks etc or write your custom implementation. But in the real life if you are using for example reentrant locks then there is almost no chance to get exception there. Even static analyzers will say you that there are no points in RLock where it is possible for exception to be thrown (as checked as unchecked). But what is possible is gettingan Error (https://docs.oracle.com/javase/7/docs/api/java/lang/Error.html) there. And again we come to a fatal JVM failure after which you wont need any locks. And instead of bytecode's monitorenter RLock and almost all other OOB java locks use AbstractQueuedSynchronizer (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/AbstractQueuedSynchronizer.html). So you can get sure that it is completely programmatic and JVM knows almost nothing about that.
Now from architecture perspective. If in some implementation you've got an unexpected exception inside the lock method and after that lock is still available for further usage then may be it would be better to get a forever living lock there instead of a lock with broken internal state. Its not safe to use it anymore and nobody guarantee correct further locking cause u have at least one precedent of incorrect behaviour. Any unexpected exception in lock should be considered as an issue which need deep investigation for it's initial reasons before further usage. And long living lock will prevent it's usage by other threads and, what is more important, system will preserve it's correct state. Then of course one day smb will m Concurrent computations in general are mostly about correctness.
Now about this question:
is there any chance that executing thread can be somehow terminated after executing lock method but before entering try-finally block?
Answer for this is of course yes. You can even suspend thread holding the lock or just call sleep so other threads wont be able to acquire it. This is how locking algorithms work and nothing we can do about that. This will be classified as a bug. Actually lock-free 2+ threads algorithms are not vulnerable for such cases. Concurrent programming is not a simple thing. There are lot of things that you should think during it's design and even after that you wont avoid failures.
Upvotes: 3