Question

Consider the "double-check idiom for lazy initialization of instance fields":

// Item 71 in Effective Java copied from this interview with Bloch.
private volatile FieldType field;
FieldType getField() {
    FieldType result = field;
    if (result == null) { // First check (no locking)
        synchronized(this) {
            result = field;
            if (result == null) // Second check (with locking)
                field = result = computeFieldValue();
        }
    }
     return result;
}

I want to be able to reset the field in a safe way (force it to load again from the database, in my case). I assume that we could do this by having a reset method:

void reset() {
   field = null;
}

Is this the standard way of doing resetting the field? Is it safe? Any pitfalls? I'm asking because Bloch gave the following warning about double-checked lazy-loading: "The idiom is very fast but also complicated and delicate, so don't be tempted to modify it in any way. Just copy and paste -- normally not a good idea, but appropriate here."

Thanks in advance, Playa from the Himalayas.

Was it helpful?

Solution

Yes, this is thread safe.

The synchronized block is to prevent multiple threads from unnecessarily calling computeFieldValue(). Since field is volatile, the accesses in reset and getField are all well-ordered.

If the first check is non-null, getField is done; result is returned.

Otherwise, a lock is acquired, excluding any other thread that might set the field to non-null, but permitting any thread to set field to null. If any thread does set field to null, nothing should have changed; that's the condition that got the thread into the synchronized block. If another thread had already acquired the lock after the current thread's check, and set the field to a non-null value, the second check will detect that.

OTHER TIPS

I think this should be safe, but only because you're storing the field in a local variable. After this is done, there's no way for the local variable reference to magically change to null, even if another thread is resetting field's value half-way through.

It seems like this will work as long as the reset method is the reset() method listed above. However, if the reset() method instantiates a new Object (like below), couldn't you end up potentially returning something different than you intended?

void reset() {
    field = new FieldType();
}

I guess it depends on exactly what you mean by thread-safe.

You could end up with a situation where a first instance is used after a second. That may be okay, or it may not.

I think reset() method isn't correct. If you read Item 71, you'll find:

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.

Lazy initialization does't suppose that field might changed. If field will be set to null between these operators:

FieldType result = field;
if (result == null) { // First check (no locking)

getField() provide incorrect result.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top