Double checked locking (yet another attempt to get around)

E

Ed

Hi all,

I'm sure this has been discussed before, here is what I have:

private static boolean propsLoaded = false;
private static Object propsGuard = new Object();

private Properties props = null;
 
J

Jon Skeet

Ed said:
I'm sure this has been discussed before, here is what I have:

Yes it has. It's just not going to work. In your case, propsLoaded can
end up being true before the properties have actually been loaded, due
to JIT compiler write reordering.

I would rather not use the alternative of a static initializer to load
the properties file since it does not support access to any logging
facilities.

In what way? You can use logging facilities from a static initializer
as you can from anywhere else - but beware of doing stuff that's going
to end up needing the properties already, etc.
Essentially I would like to read the properties only once
upon the first invocation.

Then lock on an object which is created just for that purpose
(propsGuard in your example), and lock on it *every* time. Yes, it's a
tiny performance hit. Do you have any reason to believe it'll actually
affect performance significantly?
 
S

soft-eng

Hi all,

I'm sure this has been discussed before, here is what I have:

private static boolean propsLoaded = false;
private static Object propsGuard = new Object();

private Properties props = null;

.
.
.

protected someMethod()
{
if (!propsLoaded)
{
synchronized(propsGuard)
{
if (props == null)
{
// Only get properties file if we have never done so
props = getProperties_(PROPERTIES_FILE);
}
log("I have loaded the properties file.");
}
if (props != null)
propsLoaded = true;
}
.
.
}


The problem appears to be that it's legal for the environment to
move the code
if (props != null)
propsLoaded = true;

inside the sync block! So you are back into the base case,
and propsLoaded (the initial check) may be true yet getProperties may not
have completed. In fact, theoretically it's possible
for a second thread to see "props" null, but "propsLoaded" true,
because when and what order the second thread gets to see the first
thread's (internal copies of) variables, is undefined!
(Yes, it breaks the basic intuition, and I think
maybe somebody could do a Ph.D. thesis on what other
common programming logic could turn out to be broken
because of this.)

Jon Skeet had posted a method using a nested
class, where you do an impllicit synchronization by
taking advantage of the VM's internal locks which
should be faster than doing explicit synchronization:

http://www.pobox.com/~skeet/csharp/singleton.html (# 5)
 
E

Ed Yu

Thanks for your insight. How about moving the statement after the sync
block:

if (props!=null)
propsLoaded = true;

to method getProperties_(PROPERTIES_FILE) right before returning. This way
the compiler will not be able to re-order it.
 
J

Jon Skeet

Ed Yu said:
Thanks for your insight. How about moving the statement after the sync
block:

if (props!=null)
propsLoaded = true;

to method getProperties_(PROPERTIES_FILE) right before returning. This way
the compiler will not be able to re-order it.

What makes you think that?

Very, *very* smart people have basically said "Double-check locking in
Java doesn't work." Lots of people have come up with ways to try to
make it work. I don't believe anyone's found one which works yet.

Are you *really* sure you need to solve this? Have you measured the
performance degredation if you just synchronize every time?
 
J

Jon Skeet

Ed said:
Thanks for your reply. I would simply like to know if I move the code
that set the propsLoaded flag inside the method
getProperties_(fileName), will the compiler have the ability to
re-order the statement setting the propsLoaded variable.

I'm not suggesting that I can solve the double checked locking
problem, I don't intend to, I'm only interested in knowing if my code
works for my case here. If it does not work, please educate me with
the reason why since I'm not familiar with the JVM specification and
Java threading for that matter. Again thanks for your input.

It won't work precisely because you *are* suggesting that you can solve
the double-checked locking problem - you're attempting to use it.

The reason it could fail is that propsLoaded can be set to true before
the rest of props is made available to other threads - so you could end
up with other threads checking propsLoaded, seeing it as "true" and
trying to use the not-fully-initialised properties.
 
E

Ed

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.
 
X

xarax

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.
 
S

soft-eng

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.

You can trust ordering of code within any one thread.
You cannot trust ordering of code between two threads.
That's obvious, what's not obvious but the case
in Java, is that you can't depend upon one thread's
sequential view of what the other thread should be doing!
If you think thread 1 as something sitting inside the
machine watching thread 2, thread 1 will expect to
see thread 2 doing

statement 1
statement 2
statement 3

but instead thread 1 could very well see

statement 3
statement 1
statement 2

or some other order which makes no sense to thread 1!

Mostly this is because thread 2 has a "private" view of
memory. It's doing things in its internal world,
and bothering to update the external world only
when it thinks necessary. But its definition
of "necessary", does not relate to thread 2's
definition of "necessary".

Memory synchronization in java is an extra job
being performed by synchronization,
beyond its normal job of logic synchronization.

It all does seem a little too Sun-hw-centric (which I
understand actually does give each processor its own task-copy
of memory, only to be synchronized with global memory at
specified points.) But then again, Java was developed
at Sun, so that should not be a surprise. (I don't
know if any other hw does this sort of optimization.)
 
E

Ed

Thanks so much for all the inputs. It is very clear now that it won't
work simply because of other threads does not neccessary sees memory
modifications in the coded sequences.

Again thanks for all the patience and explaination.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top