I read this in an upvoted comment on StackOverflow:
But if you want to be safe, you can add simple synchronized(this) {} at the end of you @PostConstruct [method]
[note that variables were NOT volatile]
I was thinking that happens-before is forced only if both write and read is executed in synchronized
block or at least read is volatile.
Is the quoted sentence correct? Does an empty synchronized(this) {}
block flush all variables changed in current method to "general visible" memory?
Please consider some scenerios
what if second thread never calls lock on this
? (suppose that second thread reads in other methods). Remember that question is about: flush changes to other threads, not give other threads a way (synchronized) to poll changes made by original thread. Also no-synchronization in other methods is very likely in Spring @PostConstruct
context - as original comment says.
is memory visibility of changes forced only in second and subsequent calls by another thread? (remember that this synchronized block is a last call in our method) - this would mark this way of synchronization as very bad practice (stale values in first call)
No. If a object has synchronized instance methods then the Object itself is used a lock object for controlling the synchronization. Therefore all other instance methods need to wait until previous method call is completed.
A synchronized statement can be used to acquire a lock on any object, not just this object, when executing a block of the code in a method. This block is referred to as a synchronized block.
To make it thread safe then, you have to force person 1 to wait for person 2 to complete their task before allowing person 1 to edit the document. Synchronized means that in a multiple threaded environment, a Synchronized object does not let two threads access a method/block of code at the same time.
When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.
Much of what's written about this on SO, including many of the answers/comments in this thread, are, sadly, wrong.
The key rule in the Java Memory Model that applies here is: an unlock operation on a given monitor happens-before a subsequent lock operation on that same monitor. If only one thread ever acquires the lock, it has no meaning. If the VM can prove that the lock object is thread-confined, it can elide any fences it might otherwise emit.
The quote you highlight assumes that releasing a lock acts as a full fence. And sometimes that might be true, but you can't count on it. So your skeptical questions are well-founded.
See Java Concurrency in Practice, Ch 16 for more on the Java Memory Model.
All writes that occur prior to a monitor exit
are visible to all threads after a monitor enter
.
A synchronized(this){}
can be turned into bytecode like
monitorenter monitorexit
So if you have a bunch of writes prior to the synchronized(this){}
they would have occurred before the monitorexit
.
This brings us to the next point of my first sentence.
visible to all threads after a
monitor enter
So now, in order for a thread to ensure the writes ocurred it must execute the same synchronization ie synchornized(this){}
. This will issue at the very least a monitorenter
and establish your happens before ordering.
So to answer your question
Does an empty synchronized(this) {} block flush all variables changed in current method to "general visible" memory?
Yes, as long as you maintain the same synchronization when you want to read those non-volatile variables.
To address your other questions
what if second thread never calls lock on this? (suppose that second thread reads in other methods). Remember that question is about: flush changes to other threads, not give other threads a way (synchronized) to poll changes made by original thread. Also no-synchronization in other methods is very likely in Spring @PostConstruct context
Well in this case using synchronized(this)
without any other context is relatively useless. There is no happens-before relationship and it's in theory just as useful as not including it.
is memory visibility of changes forced only in second and subsequent calls by another thread? (remember that this synchronized block is a last call in our method) - this would mark this way of synchronization as very bad practice (stale values in first call)
Memory visibility is forced by the first thread calling synchronized(this)
, in that it will write directly to memory. Now, this doesn't necessarily mean each threads needs to read directly from memory. They can still read from their own processor caches. Having a thread call synchronized(this)
ensures it pulls the value of the field(s) from memory and retrieve most up to date value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With