Reputation: 1249
Can the synchronization statements be reordered. i.e : Can :
synchronized(A) {
synchronized(B) {
......
}
}
become :
synchronized(B) {
synchronized(A) {
......
}
}
Upvotes: 1
Views: 505
Reputation: 8415
Synchronized statements are never reordered by the compiler as it has a big effect on what ends up happening.
Synchronized blocks are used to obtain a lock on the specific Object placed between the synchronized parenthesis.
private final Object LOCK_1 = new Object();
public void foo(){
synchronized(LOCK_1){
//code here...
}
}
Obtains the lock for Object LOCK_1 and releases it when the synchronization block completes. Since synchronization blocks are used to guard against concurrent access it may be sometimes required to use multiple locks especially when multiple thread-unsafe objects are being written/read to/from.
Consider the following code that uses a nested synchronization block:
private final Object LOCK_1 = new Object();
private final Object LOCK_2 = new Object();
public void bar(){
synchronized(LOCK_1){
//Point A
synchronized(LOCK_2){
//Point B
}
//Point C
}
//Point D
}
If we look at points A,B,C,D we can realize why the order of synchronization matters.
First at point A, the lock for LOCK_1 is obtained therefore any other threads trying to obtain LOCK_1 is put into a queue.
At point B, the currently executing thread owns the lock for both LOCK_1 and LOCK_2.
At point C, the currently executing thread has released the lock for LOCK_2
At point D, the currently executing thread has released all locks.
If we flip this example around and decided to put LOCK_2 on the outer block, you will realize that the thread's order of obtaining locks changes which has a big effect on what it ends up doing. Normally, when I make programs with synchronization blocks I use one MUTEX object per thread-unsafe resource I am accessing (or one MUTEX per group). Say I want to read from a stream using LOCK_1 and write to a stream using LOCK_2. It would be illogical to think that swapping the locking order around means the same thing.
Consider that LOCK_2 (the writing lock) is being held by another thread. If we have LOCK_1 on the outer block the currently executing thread can at least process all the reading code before being put into a queue for the writing lock (essentially, the ability to execute code at point A). If we flipped the order of the locks around, the currently executing thread will end up having to wait for the writing is complete, then proceed onto reading and writing whilst holding onto the writing lock (all the way through reading too).
Another problem that comes up when the order of locks are switched (and not consistently, some code has LOCK_1 first and others have LOCK_2 first). Consider that two threads both eagerly try to execute code which have different locking orders. Thread 1 obtains LOCK_1 in the outer block and thread 2 obtains LOCK_2 from the outer block. Now when thread 1 tries to obtain LOCK_2, it can't since thread 2 has it. And when thread 2 tries to obtain LOCK_1, it can't either because thread 1 has it. The two threads essentially block on each other forever, forming a deadlock situation.
To answer your question, if you want to lock on two objects immediately without doing any sort of processing between locks then the order is irrelevant (essentially no processing at point A or C). HOWEVER it is essential to keep the order consistent throughout your program as to avoid deadlocking.
Upvotes: 0
Reputation: 116938
Can the synchronization statements be reordered?
I assume you are asking if the compiler can reorder the synchronized
blocks so the lock order happens in a different order than the code.
The answer is no. A synchronized
block (and a volatile
field access) impose ordering restrictions on the compiler. In your case, you cannot move a monitor-enter before another monitor-enter nor a monitor-exit after another monitor-exit. See the grid below.
To quote from JSR 133 (Java Memory Model) FAQ:
It is not possible, for example, for the compiler to move your code before an acquire or after a release. When we say that acquires and releases act on caches, we are using shorthand for a number of possible effects.
Doug Lea's JSR-133 Cookbook has a grid which shows the reordering possibilities. A blank entry in the grid means that reordering is allowed. In your case, entering a synchronized
block is a "MonitorEnter" (same reordering limitations as loading of a volatile
field) and exiting a synchronized
block is a "MonitorExit" (same as storing to a volatile
field).
Upvotes: 5
Reputation: 60848
Yes and no.
The order must be consistent.
Suppose you are creating a transaction between two bank accounts, and always grab the sender's lock first, then grab the receiver's lock. Problem is - say both Dan and Bob want to transfer money to each other at the same time.
Thread 1 might grab Dan's lock, as it processes Dan's transaction to Bob.
Then thread 2 grab's Bob's lock, as it processes Bob's transaction to Dan.
Then, bam, deadlock.
The morals are:
So this is the part of the answer where I guess at other things you might have been trying to ask instead, because the expectation is firmly on me that I act psychic.
The JVM will not acquire the locks in an order different from which you have programmed. How do I know this? Because otherwise it would not be possible to solve the problem in the first half of my answer.
Upvotes: 0