Brian Goetz mentions about "because synchronization was not used to make the Holder visible to other threads, we say the Holder was not properly published". I'm terribly confused here. Is it because reference to Holder is stored to public field (and any thread could get reference to that, whatever it's state)? Or, if I have understood correctly java memory model doesn't guarantee that when retrieving object reference it's constructor has actually finished? This topic is also considered in https://www.securecoding.cert.org/confluence/display/java/TSM03-J.+Do+not+publish+partially+initialized+objects
If it's the latter it's very scary. That would mean that most of the java programs out there have threading issues..Also if that's the case, how would instance variables constructed at "class" level behave? I mean following situation..
Is it because reference to Holder is stored to public field (and any thread could get reference to that, whatever it's state)?
As the reference is public so any thread can get it, whatever its state may be.
Or, if I have understood correctly java memory model doesn't guarantee that when retrieving object reference it's constructor has actually finished?
Consider an example
In th above code reference 'a' is set to this reference before 'n' is initalized and as 'a' is public therefore any thread can access it any time.
Suppose thread1 is creating an object of class A and before 'n' is initialized ie just after the execution of below statement
thread2 accesses public reference 'a' then it will refer the partially initialized object with n having the default value.
Joined: Jan 06, 2006
Thank you Nomaan Butt
I think I finally figured this out after I posted the topic, your reply assures my assumption..
I was so confused about that "when object is constructed Object constructor is run and all member variables are set to their default values, thus Holder could have 'stale' member variable values " which is also mentioned in the book. But now it makes sense. It's all about the public reference and threads interfering with each other..
No, there actually was also something very important here:
Tapio Niemela wrote:Or, if I have understood correctly java memory model doesn't guarantee that when retrieving object reference it's constructor has actually finished?
Indeed, the Java memory model does not make such a guarantee - at least not for the code shown. It makes such a guarantee for final fields, but not for nonfinal fields. Even if those nonfinal fields are never intentionally changed after the constructor completes. If you want to use nonfinal fields in a thread-safe manner, there needs to be some synchronization* involved before the reference is published (i.e. before it's written to a public static field, for example). Otherwise weird, counterintuitive things can happen in the JVM.
* In Goetz' book the term "synchronization" is used in a general way to include using the synchronized keyword, the volatile keyword, atomic and locking classes from the java.util.concurrent packages, and possibly a few other mechanisms I've forgotten. So it doesn't necessarily imply you need the synchronized keyword, but something along those lines.
Joined: Jan 06, 2006
Thanks for reply Mike,
Yes, I understand also what you are saying, but the case with the Holder example isn't just with final fields. (Although it can be fixed by declaring holder to be final, or field 'n' of the Holder class final). It was bit unclear what Goetz ment..
This is what I have understood. Please correct me if I have misunderstood..
Now without synchronization this could happen
1. Thread a constructs new Holder by calling HolderHolder.initialize. First, constructor of Object class is called. This will set the n to 0, after that Holder constructor sets it to 42.
2. Meanwhile thread b constructs also new Holder, but with same reference. It will also call Object constructor and set fields to default values
3. Now thread a starts assertSanity method
4. n is now 0 (set by thread b)
5. thread a reads n value (which is 0)
6. thread b continues with Holder constructor, setting n to 42
7. thread a reads n value again, which is now 42
Also I don't think that simply declaring holder to be volatile would be sufficient, because it is never checked if the holder is constructed (not null). Instead some sort of double-checked locking should be used. Am I wrong?
Thanks for the patience
Joined: Oct 19, 2011
In the above code you are creating two different thread objects, each Test thread has separate HolderHolder object and separate Holder object and therefore separate integer field 'n'. Therefore none will interfere.
If 'n' is static then only there can be interference, volatile will also not have any effect here as objects are different.
This example comes under "A reference to the object containing the final field did not escape the constructor"
When you instantiate a new Holder object with the new operator,
1) the Java virtual machine first will allocate (at least) enough space on the heap to hold all the instance variables declared in Holder and its superclasses.
2) Second, the virtual machine will initialize all the instance variables to their default initial values.
3) Third, the virtual machine will invoke the <init> method in the Holder class.
Assume: 1st Thread starts 10:00 am, it calls instatied the Holder object by making the call of new Holer(42),
1) the Java virtual machine first will allocate (at least) enough space on the heap to hold all the instance variables declared in Holder and its superclasses. -- it will 10:01 time
2) Second, the virtual machine will initialize all the instance variables to their default initial values -- it will start 10:02 time
3) Third, the virtual machine will invoke the <init> method in the Holder class.-- it will start 10:04 time
Now Thread2 started at --> 10:02:01 time, and it will make a call assertSanity() 10:03, by that time n was initialized with default of Zero, Second thread reading the stale data.
public Holder holder;
if you make the public final Holder holder will resolve this issue
private int n; if you make the private final int n; will resole this issue.