5

I am reading about double check locking from Effective Java. The code does the following:

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;  
}    

It says that using result seems unneeded but actually ensures that the field is only read only once in the common case where it is already initialized.

But I don't understand this. What is the difference with doing if(field == null) directly? I don't understand why if (result == null) is different, let alone better as stated.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
Jim
  • 18,826
  • 34
  • 135
  • 254

2 Answers2

5

The explanation is on the next page (emphasis by me):

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.

For reference, the quote is from p. 284 of Item 71: Use lazy initialization judiciously in Effective Java 2nd Edition.

Update: the difference between reading a local vs a volatile variable is that the former may be optimized better. Volatile variables can't be stored in registers or caches, nor memory operations on them reordered. Moreover, reading a volatile variable may trigger memory synchronization between different threads.

See Java Concurrency in Practice, section 3.1.4: Volatile Variables for more details.

Péter Török
  • 114,404
  • 31
  • 268
  • 329
4

The thinking in that example is that result/field will be used more than once further down I guess. Accessing result is cheaper (it's not volatile).

You have a second volatile read when doing the return otherwise.

Use the initializaton on demand holder pattern instead if you need to do this. http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom

Adding some of my clarifications in the comments to the answer itself for well... clarity:

Short version: A local variable can just be in a register in (one of) the cpu(s) (and in one of the cpu's cores if multiple etc). That's as fast as it gets. A volatile variable must be checked for changes in other cores/caches/cpus/memory, but the details can be very hardware specific (cache lines, memory barriers etc). But also jvm specific, (the hotspot server compiler might hoist non volatile variables for example) and it imposes limits on reordering instructions for possible performance gains as well

Mattias Isegran Bergander
  • 11,811
  • 2
  • 41
  • 49
  • You might want to mention what `volatile` implies, in Java: http://en.wikipedia.org/wiki/Volatile_variable#In_Java – ArjunShankar May 09 '12 at 09:04
  • @Pulsar:So it is more expensive to access a `volatile`? – Jim May 09 '12 at 09:08
  • @Jim - yes. Read the link I pasted above. – ArjunShankar May 09 '12 at 09:08
  • Well that's a rather complicated subject though, for the details I mean. But a good start would be the java memory model http://en.wikipedia.org/wiki/Java_Memory_Model – Mattias Isegran Bergander May 09 '12 at 09:09
  • Short version: A local variable can just be in a register in (one of) the cpu(s) (and in one of the cpu's cores if multiple etc). That's as fast as it gets. A volatile variable must be checked for changes in other cores/caches/cpus/memory, but the details can be very hardware specific (cache lines, memory barriers etc). But also jvm specific, (the hotspot server compiler might hoist non volatile variables for example) and it imposes limits on reordering instructions for possible performance gains as well. – Mattias Isegran Bergander May 09 '12 at 09:13
  • @Pulsar:But during the assignment to `result` isn't the `volatile field` accessed anyway?So don't we have the overhead access anyway? – Jim May 09 '12 at 09:21
  • For short, reading a volatile is like entering a synchronized block, and writing to it is like leaving a synchronized block – Mattias Isegran Bergander May 09 '12 at 09:24
  • Yes @Jim and that is why I said "The thinking in that example is that result/field will be **used more than once** further down I guess." So we only have it once instead of multiple. But we didn't have the entire code block from you then :) – Mattias Isegran Bergander May 09 '12 at 09:26
  • 1
    You have a second volatile read when doing the return otherwise. – Mattias Isegran Bergander May 09 '12 at 09:29