I have a question about how to make an object guaranteed to be thread-safe by the Java Memory Model.
I have read a lot which say that writing a synchronized scope in a constructor does not make sense, but why doesn't it? Yes, it is true that as long as the object under construction is not shared among threads (, which it shouldn't be), no threads other than the constructing one can reach any synchronized(this){...}, so there are no need to make that scope in the constructor in order to exclude them. But synchronized scopes are not only for exclusion; they are also used to create happens-before relationships. JLS.17.4
Here is a sample code to make my point clear.
public class Counter{
private int count;
public Counter(int init_value){
//synchronized(this){
this.count = init_value;
//}
}
public synchronized int getValue(){
return count;
}
public synchronized void addValue(){
count++;
}
}
Think about the case where a thread t0 creates a Counter object and another thread t1 uses it. If there were the synchronized statement in the constructor, it would obviously be guaranteed to be thread-safe. (Since all actions in synchronized scopes have a happens-before relationship with each other.) But if not, i.e. no synchronized statement, does the Java Memory Model still guarantee that the initializing write by t0 of count can be seen by t1? I think not. It is just like f.y can see 0 in the sample code 17.5-1 in JLS.17.5. Unlike the case of JSL.17.5-1, now the second thread accesses the field only from synchronized methods, but I think synchronized statements have no guaranteed effect in this situation. (They don't create any happens-before relationship with any action by t0.) Some say that the rule about a happens-before edge at the end of a constructor guarantees it, but the rule seems to be just saying a constructor happens-before finalize().
Then should I write the synchronized statement in the constructor to make the object thread-safe? Or are there some rules or logics about the Java Memory Model I have missed and actually no need for that? If I am true, even Hashtable of openjdk (though i know it is obsolete) seems not to be thread-safe.
Or am I wrong about the definition of being thread-safe and about the policy for concurrency? If I transfer that Counter object from t0 to t1 by a thread-safe way, e.g. through a volatile variable, there seems to be no problem. (In that case, the construction by t0 happens-before the volatile write, which happens-before the volatile read by t1, which happens-before everything t1 does to it.) Should I always transfer even thread-safe objects (but not immutable) among threads through a way that causes a happens-before relationship?
Counter
before the constructor returns on t0? – Narcissus