为什么在Joshua Bloch Effective Java示例中,双重检查锁定的速度提高了25%

时间:2013-06-18 08:48:29

标签: java multithreading java-ee volatile

以下是Effective Java 2nd Edition的片段。在这里,作者声称下面的代码比你不使用结果变量快25%。 根据书中的“这个变量的作用是确保该字段在已经初始化的常见情况下只读取一次。” 。 我无法理解为什么这个代码在初始化之后会比较快,如果我们不使用Local变量结果。在任何一种情况下,无论是否使用局部变量结果,初始化后只有一个易失性读取。

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

2 个答案:

答案 0 :(得分:9)

初始化field后,代码为:

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

或:

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

在第一种情况下,您读取两次volatile变量,而在第二种情况下,您只读取一次。虽然易失性读取非常快,但它们比从局部变量读取要慢一些(我不知道它是否为25%)。

注意:

  • 易失性读取与最近处理器(至少x86)/ JVM上的正常读取一样便宜,即没有区别。
  • 但是编译器可以更好地优化代码而不使用volatile,这样您就可以从更好的编译代码中获得效率。
  • 无论如何,几纳秒的25%仍然不多。
  • 这是一个标准的习惯用法,可以在java.util.concurrent包的许多类中找​​到 - 参见例如this method in ThreadPoolExecutor(其中有很多)

答案 1 :(得分:0)

在不使用局部变量的情况下,在大多数调用中我们都有效

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

因此有两个易失性读取,比一个易失性读取慢。

实际上,JVM可以将两个易失性读取合并为一个易失性读取,并且仍然符合JMM。但是我们希望JVM每次被告知时都要执行一个良好的易失性读取,而不是智能手机,并试图优化掉任何易失性读取。考虑一下这段代码

volatile boolean ready;

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

我们希望JVM能够重复加载变量。