How to Solve the "Double-Checked Locking Is Broken" Declaration in Java

How to solve the Double-Checked Locking is Broken Declaration in Java?

Here is the idiom recommended in the Item 71: Use lazy initialization judiciously of
Effective Java:

If you need to use lazy initialization for performance on an
instance field, use the double-check
idiom
. This idiom avoids the cost
of locking when accessing the field
after it has been initialized (Item
67). The idea behind the idiom is to
check the value of the field twice
(hence the name double-check): once
without locking, and then, if the
field appears to be uninitialized, a
second time with locking. Only if the
second check indicates that the field
is uninitialized does the call
initialize the field. Because there is
no locking if the field is already
initialized, it is critical that the
field be declared volatile (Item
66). Here is the idiom:

// Double-check idiom for lazy initialization of instance fields
private volatile FieldType field;

private FieldType getField() {
FieldType result = field;
if (result != null) // First check (no locking)
return result;
synchronized(this) {
if (field == null) // Second check (with locking)
field = computeFieldValue();
return field;
}
}

This code may appear a bit convoluted.
In particular, the need for the local
variable result may be unclear. What
this variable does is to ensure that
field is read only once in the common
case where it’s already initialized.
While not strictly necessary, this may
improve performance and is more
elegant by the standards applied to
low-level concurrent programming. On
my machine, the method above is about
25 percent faster than the obvious
version without a local variable.

Prior to release 1.5, the double-check
idiom did not work reliably because
the semantics of the volatile modifier
were not strong enough to support it
[Pugh01]. The memory model introduced
in release 1.5 fixed this problem
[JLS, 17, Goetz06 16]. Today, the
double-check idiom is the technique of
choice for lazily initializing an
instance field. While you can apply
the double-check idiom to static
fields as well, there is no reason to
do so: the lazy initialization holder
class idiom is a better choice.

Reference

  • Effective Java, Second Edition

    • Item 71: Use lazy initialization judiciously

Double checked locking pattern: Broken or not?

It looks okay at first glance, but this technique has many subtle problems and should usually be avoided. For example, consider the following sequence of events:

  1. Thread A notices that the value is
    not initialized, so it obtains the
    lock and begins to initialize the
    value.
  2. The code generated by the compiler is allowed
    to update the shared variable to
    point to a partially constructed
    object before A has finished
    performing the initialization.
  3. Thread B notices that the shared
    variable has been initialized (or so
    it appears), and returns its value.
    Because thread B believes the value
    is already initialized, it does not
    acquire the lock. If B uses the
    object before all of the
    initialization done by A is seen by
    B the program will likely crash.

You could avoid this by using the "volatile" keyword to handle your singleton instances correctly

Why is double-checked locking broken in Java?

The problem is not atomicity, it's ordering. The JVM is allowed to reorder instructions in order to improve performance, as long as happens-before is not violated. Therefore, the runtime could theoretically schedule the instruction that updates helper before all instructions from the constructor of class Helper have executed.

is Double-Checked Locking is Broken a java-only thing?

Double checked locking is safe in Java, PROVIDED THAT:

  1. the instance variable is declared as volatile, AND
  2. the JVM correctly implements the JSR-133 specification; i.e. it is compliant with Java 5 and later.

My source is the JSR-133 (Java Memory Model) FAQ - Jeremy Manson and Brian Goetz, February 2004. This is confirmed by Goetz in a number of other places.

However, as Goetz says, this is an idiom whose time has passed. Uncontended synchronization in Java is now fast, so he recommends that you just declare the getInstance() method as synchronized if you need to do lazy initialization. (And I imagine that this applies to other languages too ...)

Besides, all things being equal, it is a bad idea to write code that works in Java 5 but is unreliable in older JVMs.


OK, so what about the other languages? Well, it depends on how the idiom is implemented, and often on the platform.

  • C# - according to https://stackoverflow.com/a/1964832/139985, it is platform dependent whether the instance variable needs to be volatile. However, Wikipedia says that if you do use volatile or explicit memory barriers, the idiom can be implemented safely.

  • VB - according to Wikipedia the idiom can be implemented safely using explicit memory barriers.

  • C++ - according to Wikipedia the idiom can be implemented safely using volatile in Visual C++ 2005. But other sources say that in general the C++ language specification doesn't provide sufficient guarantees for volatile to be sure. However double-checked locking can be implemented in the context of the C++ 2011 language revision - https://stackoverflow.com/a/6099828/139985.

(Note: I'm just summarizing some sources I found which seem to me to be recent ... and sound. I'm not C++, C# or VB expert. Please read the linked pages and make your own judgements.)

Double-checked locking without volatile

First things first: what you are trying to do is dangerous at best. I am getting a bit nervous when people try to cheat with finals. Java language provides you with volatile as the go-to tool to deal with inter-thread consistency. Use it.

Anyhow, the relevant approach is described in
"Safe Publication and Initialization in Java" as:

public class FinalWrapperFactory {
private FinalWrapper wrapper;

public Singleton get() {
FinalWrapper w = wrapper;
if (w == null) { // check 1
synchronized(this) {
w = wrapper;
if (w == null) { // check2
w = new FinalWrapper(new Singleton());
wrapper = w;
}
}
}
return w.instance;
}

private static class FinalWrapper {
public final Singleton instance;
public FinalWrapper(Singleton instance) {
this.instance = instance;
}
}
}

It layman's terms, it works like this. synchronized yields the proper synchronization when we observe wrapper as null -- in other words, the code would be obviously correct if we drop the first check altogether and extend synchronized to the entire method body. final in FinalWrapper guarantees iff we saw the non-null wrapper, it is fully constructed, and all Singleton fields are visible -- this recovers from the racy read of wrapper.

Note that it carries over the FinalWrapper in the field, not the value itself. If instance were to be published without the FinalWrapper, all bets would be off (in layman terms, that's premature publication). This is why your Publisher.publish is disfunctional: just putting the value through final field, reading it back, and publishing it unsafely is not safe -- it's very similar to just putting the naked instance write out.

Also, you have to be careful to make a "fallback" read under the lock, when you discover the null wrapper, and use its value. Doing the second (third) read of wrapper in return statement would also ruin the correctness, setting you up for a legitimate race.

EDIT: That entire thing, by the way, says that if the object you are publishing is covered with final-s internally, you may cut the middleman of FinalWrapper, and publish the instance itself.

EDIT 2: See also, LCK10-J. Use a correct form of the double-checked locking idiom, and some discussion in comments there.

Is double-checked locking only broken in a lazy initialization scene?

Answer to your 1: Yes. From Java 5 class loader guarantees the single or one time initialization for static fields.
Answer to your 2: From code its not clear but it depends what you reading in wxMpConfigStorage.isJsapiTicketExpired() method. If you deciding based on reading something which is not thread safe then yes DCL can fail. But in your case jsapiTicketExpiresTime is volatile and so thread safe to read and so DCL will work for you.

why java double check lock singleton must use the volatile keyword?

a thread may assign the instance variable before the constuctor finishes

That's not actually true. The assignment is right there in the code example:

instance=new DoubleCheckSingleton()

Obviously, the thread that performs that assignment can not possibly do the assignment before the constructor call has returned.

The problem is, when two different threads are running on two different processors without any synchronization, they will not necessarily agree upon the order in which assignments happen. So, even though thread A assigned the fields of the new object (inside the new DoubleCheckSingleton() call) before it assigned instance, thread B potentially could see those assignments out-of-order. Thread B could see the assignment to instance before it sees some of the other things that new DobuleCheckSingleton() did.

Declaring instance to be volatile synchronizes the threads. volatile guarantees that everything thread A did before it assigned a volatile variable will become visible to thread B when thread B fetches the value of the volatile variable.



Related Topics



Leave a reply



Submit