This week's book giveaway is in the OO, Patterns, UML and Refactoring forum.
We're giving away four copies of Refactoring for Software Design Smells: Managing Technical Debt and have Girish Suryanarayana, Ganesh Samarthyam & Tushar Sharma on-line!
See this thread for details.
The moose likes Developer Certification (SCJD/OCMJD) and the fly likes Nested Locks Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login

JavaRanch » Java Forums » Certification » Developer Certification (SCJD/OCMJD)
Bookmark "Nested Locks" Watch "Nested Locks" New topic

Nested Locks

Javini Javono
Ranch Hand

Joined: Dec 03, 2003
Posts: 286
Don Wood wrote
in this thread
Hi Javini,
Whenever you are dealing with nested locks, you must always get them in the same
order. Apparently, know this as you said the following:

Dead-locks are avoided by following the locking order rules: always
lock LockManager, then lock MicroData.

Since you are using nested locks correctly (always in the same order) you are
using one of the classic deadlock avoidance mechanisms. You still have several
issues to be evaluated:
* Is the flow of code obvious enough that a junior programmer will not miss the
significance of lock ordering? If I understand the code right, you have nested
locks that are acquired in different methods. This is much harder for anyone
(much less a junior programmer) to pick up on than if both synchronized blocks
were in one method.
* What happens to the concurrency when the inner lock is not available? The
thread goes to sleep still owning the outer lock. The sleeping thread can block
other threads from doing any useful work and things come to a screeching halt
until the locks are cleared.
* When you say in step 1, Gets a lock on the LockManager., does this mean a
lock on the LockManager object? If so, when the inner lock is busy, then the
thread goes to sleep owning the LockManager lock. I don't see how the thread
that wants to release the inner lock will be able to do so since the LockManager
is locked and the owner is asleep. This can't be right so I must not be
understanding what the LockManager lock is.
Perhaps its just my reading of this that is faulty but when you say

Usually, of course, clients are
operating outside of MicroData, first using the lock manager, then calling
Data.someMethod() which in turn calls the appropriate methods in MicroData.

Do you mean that the clients have the lock manager locked when they call
MicroData? If so, it seems that you can be holding the lock manager lock for
duration of doing I/O. That would seem like the system is overlocking and
concurrency is reduced.
The alternative interpretation that I see is that the clients use the lock
manager and release its lock before calling Data.someMethod() which in turn
calls the appropriate methods in MicroData. But since MicroData can then call
the LockManager that would be a violation of your contract that says the locks
are always locked in the same order.
Nested locks can be used in a deadlock safe manner. However, when you do use
them you have to answer a lot of questions. Does it truly avoid deadlock? What
are the performance implications, especially when the thread goes to sleep
holding the outer lock while waiting for the inner lock to become free? How hard
is it for someone else maintaing the code to understand the nested locks?
My understanding of the avoid nested locks rule, is that it can be done but it
will probably save you time and a lot of headaches to redesign the code to avoid
nested locks.
Thanks for taking the time to read my post and give your response.
Before continuing, here are some lock-within-lock postings that already exist:
Topic: Does nest locks really bad?
Topic: Order of releasing locks
I'll read the above links and my software, and then get back with my reply.
Thanks again,
Javini Javono
[ March 27, 2004: Message edited by: Javini Javono ]
Javini Javono
Ranch Hand

Joined: Dec 03, 2003
Posts: 286
Here is a rough overview with more specifics.
The lock manager is called Guardian.
* The Guardian methods are rarely synchronized as it is multi-threaded.
However, when it does work, it locks the guard object which keeps track of
which records are currently locked. There are other structures, such as to
keep track of deleted record numbers. All these structures are used together
and are synchronized on guard whenever any work is done.
* Some Guardian methods call into DataCore class methods, such as to see if a
record number exists within the file.
Thus, the locking order is always the following:
lock guard, then call a method in DataCore which locks on dataCore instance.
dataCore instance
The reader and writer with all its methods synchronized is called
DataCore (except that the create() methods are the only methods which are not
The create() method will use the lock manager which in turn will call back into
a synchronized method of dataCore, which, if not handled correctly, will result
in dead-lock.
The create() method needs to lock a record, whether a previously deleted record
or a record at the end of the file, prior to writing new information to it and
effectively creating this record.

So, when a record is created, and the dataCore.create() method is called,
it first locks down all data structures associated with the lock manager.
Therefore, no new threads can query the lock manager nor request that any
record be locked.
Eventually, any threads that are reading and writing using any of dataCore's
synchronized methods will finish, and the create() method will get the lock
on "this" (which is the instance of dataCore).
The lock manager, Guardian, can then be safely used, including its abilities to
call back into synchronized methods of dataCore.
To answer your question: what happens to concurrency if the inner lock is not
available? I'd say not much, if the lock is not available, the dataCore object
is already busy reading, writing, or both.
The dataCore methods are not yet optimized. I have my JUnit code working, so
that subsequent optimization to heighten concurrency can occur easily in the
near future. So, in the future, dataCore's complete methods will not be
synchronized, only the critical sections within them will be synchronized on
"this". The same applies to the createContinued() method, its critical section
will be synchronized inside the code, using the the same, dual locks:
guard and this.
Another related question I have is what other design choice exists for creating
a record?
One idea I thought of while writing this out this morning, is that I could
create a new thread to carry out the create operation; this thread would be
sent to use a different object, which then, in turn, would use the lock manager
and then create a new record; then this thread would have to use a notify
pattern to tell the original thread the results of the operation. But, I think
my design as written is simpler to understand, and I think it does not harm
concurrency. (but, of course, if I was certain, then I probably would not be
posting this type of question).
Javini Javono
Don Wood
Ranch Hand

Joined: Dec 05, 2003
Posts: 65
Looks like I invented a new term above, dealock safe should have been thread safe.
Javini said,

To answer your question: what happens to concurrency if the inner lock is not available? I'd say not much, if the lock is not available, the dataCore object is already busy reading, writing, or both.

When the inner lock is not available, this thread sleeps holding the guard lock. Isn't true that any other thread that wants the guard lock must now wait even if the work it is doing for a different record? Lock and unlcok should be fast operations but with nested locks the guard lock risks becoming a bottleneck. This is what I mean by reduced concurrency since threads doing unrelated work on other records must now wait for this thread which is asleep with the guard lock.
Have you checked out Aspose?
subject: Nested Locks
It's not a secret anymore!