Is entering synchronized block atomic?
Asked Answered
K

3

4

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

Killerdiller answered 23/3, 2015 at 18:3 Comment(9)
Synchronized blocks are not atomic! But they help prevent race conditions by offering a proper locking mechanism.Intreat
@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.Ricci
@JohnVint Another thread maybe scheduled between statements of a synchronized block, thus it is not one unprintable all-success or fail operation.Intreat
@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).Killerdiller
@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.Ender
@jameslarge What does "synchronized block of the same object" mean? Entering the critical section in which synchronizing on the same object.Ricci
@jsn I guess my point is if two threads try synchronizing on the same object, operations from one thread will appear to be done atomically to another thread. This is true when the second thread enters a synchronized block on the same object after the previous thread succeeded.Ricci
@JohnVint, Sorry, I was trying to get you to look at the problem through the eyes of a noob. They think very differently from the way experienced programmers think. When I say "foobar" to my colleagues, I don't have to qualify it with "the variable foobar" or "the object foobar" because it's obvious what I'm talking about---we're all on the same page. It's not the same talking to noobs who don't completely understand that variables and instances and object references are not all the same thing.Ender
@jameslarge :) I understand your point. It's completely fair. I know that you knew what I was talking about but after re-reading, it can be confusing. I sometimes forget my audience.Ricci
F
1

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.

Frozen answered 23/3, 2015 at 18:24 Comment(7)
No, I'm really asking if I can change the reference to object that I'm synchronizing on (providing that changing itself is in synhronized block). If the changing itself is not in synchronized block I can come up with example of inconsistency by myself - see my example response in following thread #6911307Killerdiller
Your first sentence should say, "You can reassign object while you are synchronized on object, but it's almost always a horrible mistake." Starting your answer with "You can ..." makes it look like ... is a good idea.Ender
Well good example might be that you are synchronizing on object you want to consistently change. So when the object change it is your intention to be able to change it concurrently - there is no logical reason to wait while old object finish configuration. Is there some argument why it would be horrible idea? (except answering my question negatively and give me some reference saying that synchronized block is not safe to do it)Killerdiller
@VitBernatik If your concern is blocking some threads while an old object is being configured, there are better ways to approach it, like read-write lock.Frozen
@erickson: yup I change it to use Lock now. My only concern was some basic stability of object itself. I have one function which can load new configuration object from a file. So I do not care that some pending tasks are done with old configuration. I only care that all threads are using either consistent old configuration or consistent new.Killerdiller
@VitBernatik You know, I thought more about this, and given your use case I wondered whether a simple volatile declaration would suffice. The difference is that multiple threads could be configuring a new object simultaneously, which might be a waste of effort since most of the results would be clobbered by a later assignment without ever being used. But, if you have one thread that is watching a file for changes, and making the modifications as needed, and a bunch of threads using the configuration object, this is a simple, clean solution.Frozen
But my configuration object is complex. I want to avoid inconsistency. There are threads which can modify configuration object and all has to wait before full configuration is done. Volatile is predominantly good only for single variable.Killerdiller
E
2

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.

Ender answered 23/3, 2015 at 18:56 Comment(2)
I know! And that's what I want :) The rationale is that in the synchronized block I'm only changing the object I'm synchronizing to. So if object change I do not care that other thread is still modyfing old object... But again my question is whether during changing of object to new one can happen that 2 new thread will enter synchronized and will be modifying same new object. And this depends how is implemented locking at the beginning of synchronized() blockKillerdiller
@VitBernatik beware of writing "tricky" code. Most programmers think in terms of familiar patterns. If I see foo = ...; inside a synchronized(foo){...} block, my eyes see a bug---I don't even have to think about it. If that assignment actually is necessary for the method to behave the way it is intended, that's going to be a surprise to me because it's an unfamiliar pattern. I'd bet that it's unfamiliar to a lot of experienced developers. Google for "principle of least astonishment" for why you don't want your design patterns to be unfamiliar to other developers.Ender
F
1

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.

Frozen answered 23/3, 2015 at 18:24 Comment(7)
No, I'm really asking if I can change the reference to object that I'm synchronizing on (providing that changing itself is in synhronized block). If the changing itself is not in synchronized block I can come up with example of inconsistency by myself - see my example response in following thread #6911307Killerdiller
Your first sentence should say, "You can reassign object while you are synchronized on object, but it's almost always a horrible mistake." Starting your answer with "You can ..." makes it look like ... is a good idea.Ender
Well good example might be that you are synchronizing on object you want to consistently change. So when the object change it is your intention to be able to change it concurrently - there is no logical reason to wait while old object finish configuration. Is there some argument why it would be horrible idea? (except answering my question negatively and give me some reference saying that synchronized block is not safe to do it)Killerdiller
@VitBernatik If your concern is blocking some threads while an old object is being configured, there are better ways to approach it, like read-write lock.Frozen
@erickson: yup I change it to use Lock now. My only concern was some basic stability of object itself. I have one function which can load new configuration object from a file. So I do not care that some pending tasks are done with old configuration. I only care that all threads are using either consistent old configuration or consistent new.Killerdiller
@VitBernatik You know, I thought more about this, and given your use case I wondered whether a simple volatile declaration would suffice. The difference is that multiple threads could be configuring a new object simultaneously, which might be a waste of effort since most of the results would be clobbered by a later assignment without ever being used. But, if you have one thread that is watching a file for changes, and making the modifications as needed, and a bunch of threads using the configuration object, this is a simple, clean solution.Frozen
But my configuration object is complex. I want to avoid inconsistency. There are threads which can modify configuration object and all has to wait before full configuration is done. Volatile is predominantly good only for single variable.Killerdiller
P
0

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.

Prudential answered 23/3, 2015 at 18:9 Comment(4)
yes but I do not change the value which object is holding - see that in my example thread_M changes the object itself. And this might pose a problem.Killerdiller
He's not safe. Threads 1 and 2 could both modify the new object, just as he suspects, because they are locked on two different instances.Frozen
I'm not sure what you're really trying to accomplish. You synchronize over an object -- not a handle to an object. If you have two synchronize blocks that are for the same object, then they are synchronized, but what you're doing is really scary. Seriously, synchronize methods will be a lot safer and far easier to understand.Prudential
synchronized(object) 1) allows you to synchronize on other objects than this and 2) allows you to use smaller blocks to synchronize on.Greyhen

© 2022 - 2024 — McMap. All rights reserved.