4
votes

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)?

3
Synchronized blocks are not atomic! But they help prevent race conditions by offering a proper locking mechanism.jn1kk
@jsn What do you mean they are not atomic? Two threads operating on a synchronized block of the same object will be done atomically to one another.John Vint
@JohnVint Another thread maybe scheduled between statements of a synchronized block, thus it is not one unprintable all-success or fail operation.jn1kk
@jsn: I know other threads may run. Of course my question is only related to synchronized "object". If object is final then clearly no 2 synchronized block shall be able to enter synchronized(object). And regarding thread 1,2 it shall be "atomic" (rather I would say not interleaving) to each other. But the question is if the process of locking would be safe if Thread_M can change object reference itself (although in synchronized block).Vit Bernatik
@JohnVint: What does "synchronized block of the same object" mean? Blocks (i.e., code) don't belong to objects, blocks belong to classes. The OP is wondering how two threads can enter the same synchronized block at the same time. The answer is, they can do it when they are synchronized on different objects. In the example above, that happens unintentionally because the programmer thought that synchronized(object) { ... } operates on the variable, when really it is operating on the object to which the variable happens to refer at the instant when the block is entered.Solomon Slow

3 Answers

2
votes

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.

1
votes

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.

0
votes

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.