Hi Jon,
Again thanks for your patience. I have a question though, setting the
propsLoaded variable inside the method getProperties_() right after
loading the properties and right before returning from the method.
Will the propsLoaded flag be set to true before the props is fully
initialized? If this is the case, how can we trust any ordering of
code since propsLoaded depends on the successful instantiation
(props!=null) of the props object within the method
getProperties_(fileName) right before the method returns.
/snip/
The fundamental assertion about the "double-checked locking
is broken in Java" is that the ordering of loads and stores
appears correct *ONLY* for the current thread. Other threads
may see memory modifications in a different order, unless
there are explicit memory barriers created with synchronized{}
blocks and volatile fields. Such synchronization must be the
same for all threads that are accessing/modifying the shared
memory locations.
There are implementations where an object may be instantiated
and reference returned for that object, but the initialization
processing for that object has yet to complete. Thus, a
concurrent thread may see the reference appear in shared memory
before the object is fully initialized.
There is an open question as to whether it would be sufficient
to assign the new object reference to a local variable, then
acquire/release a monitor lock on that variable in order to
force completion of initialization and flush all memory stores
to main memory. Then assign the local variable to the shared
field so that other concurrent threads will see the reference
only *after* the object is fully initialized.
Something like this:
==================================
public class Fubar
{
protected static volatile Fubar fubar;
private Fubar()
{
}
public static getFubar()
{
if(null == fubar)
{
synchronized(Fubar.class)
{
if(null == fubar)
{
Fubar myFubar;
myFubar = new Fubar();
synchronized(myFubar)
{
// finish initialization and flush memory??
}
// now make it visible to other threads?
fubar = myFubar;
}
}
}
return fubar;
}
}
==================================
The problem with this is the notion that a compliant
Java compiler can move the "fubar = myFubar;" to a location
inside the inner synchronized{} block, which will cause the
assignment of "fubar" before the memory is flushed at
the end of the synchronized{} block. There is also the
"prescient store" thing that allows the Java compiler to
perform assignments much earlier in the code stream, by
reason that the assignment is "bound to happen" anyway
without the possibility of an exception occuring between
the time of the "prescient store" and the textual occurance
of the store in the source code. Even with a volatile
field, the JLS appears to grant too much freedom to the
compiler to reorder memory reads/writes, so long as the
current thread sees no difference between the actual
reads/writes and the textual ordering of reads/writes
in the original source code.
There is on-going work to strengthen the definition of the
Java memory model so that creating memory barriers (sometimes
called "intersects") is easier (i.e., without necessarily using
synchronized{} blocks). Memory barriers are crucial in many
computer software algorithms. Java just isn't up to the job, yet.
In fact, there are mutual exclusion software algorithms that
depend on predictable concurrent memory access. I.e., fetching
the value of a field that is being concurrently assigned
by another CPU will yield the entire old value or the
entire new value, but never a "bit mixture" of old and new
values. However, such algorithms are difficult to implement
in pure Java, precisely because of the current specification of
the memory model.
The keyword "volatile" attempts to serve the purpose of
predictable ordering and consistency with concurrent threads,
but there is other language in the JLS regarding "prescient
stores" and "total ordering" that tend to confuse the issue.
For now, if two or more threads may concurrently intersect
on the instantiation of a singleton, then they must be
in a proper synchronized{} block.