Consider the following class:
class Ideone
{
private Map<String, String> m;
public Ideone(){
synchronized(this){
m = new ConcurrentHashMap<>();
}
}
public synchronized Map<String, String> getM(){
return Collections.unmodifiableMap(m); //effectively immutable
}
}
In order to allow other classes to observe Ideone
's internal state, we should publish its internal state safely (with correct synchronization). If we don't do this, it's not guaranteed that another thread read the correct value (not the default one). For example:
public volatile Ideone ideone;
public void init(){
ideone = new Ideone();
}
I think if we didn't synchronize the construction and getter, like
class Ideone
{
private Map<String, String> m;
public Ideone(){
m = new ConcurrentHashMap<>();
}
public Map<String, String> getM(){
return Collections.unmodifiableMap(m); //effectively immutable
}
}
there would be no guarantees to observe the correct state value (default value, for instance).
But as said in this answer, such synchronization is not desirable as long as it allows this
to escape.
QUESTION: Why does the synchronization in a constructor allow this
to escape?
The answer meant that the only reason for using synchronize(this)
was for the case where the this
reference had escaped from the constructor.
But that reasoning itself is incorrect. I've added another answer to that question: https://stackoverflow.com/a/34672811/981744
You have shown a case where, at first sight, your use of synchronized(this)
in the constructor is reasonable, because it indeed ensures that getM()
will the correct value for instance variable m
when invoked from another thread.
And if you didn't have these synchronized blocks, that wouldn't have been the case necessarily - it was possible for another thread to see the value null
for field m
even after the constructor was complete, because there was no happens-before relationship between the assignment to m
in the constructor on the one thread, and the read of m
in the method getM
on the other thread.
But: how did you pass the instance of Ideone
from the one thread to the other thread? If you did this in a field without any synchronization mechanism, so without any happens-before relation, then the second thread is not guaranteed to see the whole instance of Ideone
at all.
If is sees the instance, then the data in the instance if correct, but it may also see the value null
in that case.
However if you did use a synchronization mechanism to pass the instance of Ideone
, then that mechanism already created a happens-before relationship, and your use of synchronized
in the constructor and in getM
wasn't necessary anymore.
And since all safe mechanisms for passing objects between threads involve a synchronization mechanism that sets up a happens-before relationship, it is (almost) never necessary to do what you do with synchronized
in the constructor.
You have misunderstood the answer.
Let's understand what synchronized(this)
does inside a Constructor.
For the synchronized(this)
statement to be useful, two threads should be accessing a synchronized block with the same object simultaneously.
Now, one of the threads is inside the constructor, which means the object is just being created...
And for some other thread to have a reference to that object, you should have leaked the current object (this
) from the constructor in some place.
Your code does NOT leak it, but again synchronized(this)
doesn't have any value inside the constructor in your code.