10

Hi below is the snippet from Effective Java 2nd Edition. Here the author claims the following piece of code is 25% faster more than in which u do not use result variable. According to the book "What this variable does is to ensure that field is read only once in the common case where it’s already initialized." . I am not able to understand why this code would be fast after the value is initialized as compare to of if we do not use the Local variable result. In either case you will have only one volatile read after initialization whether you use the local variable result or not.

// Double-check idiom for lazy initialization of instance fields 
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;
}
Thomas Jungblut
  • 20,525
  • 5
  • 65
  • 89
veritas
  • 2,386
  • 1
  • 18
  • 30
  • 1
    Uhm, is this the first edition? Double checked locking has been discouraged for some years now – fge Jun 18 '13 at 08:53
  • And the reason is: http://stackoverflow.com/questions/4926681/why-is-double-checked-locking-broken-in-java?rq=1 – Lenymm Jun 18 '13 at 08:55
  • 5
    @fge: Not really. Since Java 5, it's actually an OK pattern: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html – Lukas Eder Jun 18 '13 at 08:56
  • 1
    @LukasEder it is largely discouraged because Java 6+ renders it basically obsolete – fge Jun 18 '13 at 08:59
  • I'm still undecided about these things. Why did [`java.io.File.toPath()`](http://hg.openjdk.java.net/lambda/lambda/jdk/file/tip/src/share/classes/java/io/File.java) use double-checked locking, introduced in Java 1.7? – Lukas Eder Jun 18 '13 at 09:01
  • @LukasEder urgh! I guess it is because they didn't really have a choice here given the many constructors of `File`... They didn't want to make the effort to make `toPath` `final` and initializing it in all of them, I guess – fge Jun 18 '13 at 09:10
  • @fge: I think lazy initialisation is important there, as the call to `FileSystems.getDefault().getPath(path);` is too expensive to be made for every `File` in the Java world... So how to lazy-initialise and do without double-checked locking or synchronisation? – Lukas Eder Jun 18 '13 at 09:22
  • 1
    @Lenymm the link you provided didn't talk about Local Variable Result. – veritas Jun 18 '13 at 09:38

2 Answers2

10

Once field has been initialised, the code is either:

if (field == null) {...}
return field;

or:

result = field;
if (result == null) {...}
return result;

In the first case you read the volatile variable twice whereas in the second you only read it once. Although volatile reads are very fast, they can be a little slower than reading from a local variable (I don't know if it is 25%).

Notes:

  • volatile reads are as cheap as normal reads on recent processors (at least x86)/JVMs, i.e. there is no difference.
  • however the compiler can better optimise a code without volatile so you could get efficiency from better compiled code.
  • 25% of a few nanoseconds is still not much anyway.
  • it is a standard idiom that you can find in many classes of the java.util.concurrent package - see for example this method in ThreadPoolExecutor (there are many of them)
assylias
  • 310,138
  • 72
  • 642
  • 762
  • ok so it means "return field" also constitutes to a volatile read (provided field is volatile) – veritas Jun 18 '13 at 09:59
  • @veritas Yes it does - if you examine the bytecode you will see that it is loaded before being returned. – assylias Jun 18 '13 at 09:59
  • +1: Nice to see an example that is independent of double-checked locking – Lukas Eder Jun 18 '13 at 11:38
  • An interesting read regarding double check locking (DCL) and safe publication can be found here: http://shipilev.net/blog/2014/safe-public-construction/#_x86_2. Per its testing, there is no performance gain on x86 by using this local variable. And on ARM, using the local variable has a performance gain around 15% over plain DCL. – lcn Nov 01 '15 at 03:08
0

Without using a local variable, in most invocations we have effectively

if(field!=null) // true
    return field;

so there are two volatile reads, which is slower than one volatile read.

Actually JVM can merge the two volatile reads into one volatile read and still conform to JMM. But we expect JVM to perform a good faith volatile read every time it's told to, not to be a smartass and try to optimize away any volatile read. Consider this code

volatile boolean ready;

do{}while(!ready); // busy wait

we expect JVM to really load the variable repeatedly.

ZhongYu
  • 18,818
  • 5
  • 31
  • 55