简体   繁体   中英

Is entering synchronized block atomic?

Do you know if there is guaranteed that synchronized block in java is atomic?

Imagine following case

Thread_1,2:

synchronized(object){object.modify();}

(object is shared variable.)

imagine thread_M will change reference to object like

synchronized(object){object = new Object()}

now imagine threads 1 and 2 are competing over getting the lock on object

Is it possible that following would happened:
1. Thread1: read old object
2. ThreadM: modify object reference & release old object lock
3. Thread2: read new object; check lock; lock on it
4. Thread1: check lock (ok cos old object was read); lock on it
now both threads have a lock and modify same (new) object

So to specify my question - is somewhere guaranteed that in synchronized(object) steps (1 and 4) are atomic (like depicted in step 3)?

Suppose you have some variable, foo :

Foo foo;

And suppose it holds a reference to an object:

foo = new Foo(...);

And suppose we have a synchronized block:

synchronized(foo) {
    ...
}

The synchronized keywoord does not operate on the variable, foo , and it does not operate on the statements in the synchronized block.

The only thing that the synchronized keyword does here is it prevents other threads from synchronizing on the same instance at the same time.

If you reassign the variable, foo to refer to some different instance while thread A is inside the block, then some other thread B will be able to enter the same block at the same time because each of the two threads will be synchronized on a different instance.

You can reassign object while you are synchronized on object , but I can't think of a scenario where reassigning a field used for locking is a good idea.

No other thread will be able to acquire the lock on the old value of object until thread M exits its synchronized block, but another thread will be able to acquire a lock on the new object as soon as it is visible to that thread.

Modifications made by a thread before releasing a lock are guaranteed to be visible to threads that acquire the lock afterwards. But since you are reassigning the lock itself, the acquiring thread may not see that it has been changed, and acquire a lock on the old value. Then they would still not see that object has been reassigned.

Declaring object as a volatile variable would ensure that its "current" value is used for locking. But it wouldn't prevent two threads from modifying the same instance concurrently:

  1. Thread M acquires lock on old value. Thread 1 reads the old value.
  2. Thread M changes the value.
  3. Thread M releases lock on old value. Thread 2 reads the new value.
  4. Thread 1 acquires lock on old value. Thread 2 acquires lock on new value.
  5. Thread 1 reads new value. Thread 2 reads new value.
  6. Thread 1 modifies new value. Thread 2 modifies new value.

To avoid all of this, just create a separate object for locking, and never change it.

You are synchronizing over the object that that "object" points to, not the variable holding the value.

However, because both pieces of code synchronize on your object before moving forward, you're safe -- although this is a poor design pattern.

You may find less confusion if you use synchronized methods instead of synchronized code blocks.

Also, just my opinion, but synchronized(object) seems like a VERY poor design pattern. That's just my opinion, but I never do something like that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM