10

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

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

2 回答 2

10

初始化field后,代码为:

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

或者:

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

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

笔记:

  • volatile 读取与最近处理器(至少 x86)/JVM 上的普通读取一样便宜,即没有区别。
  • 但是,编译器可以更好地优化没有 volatile 的代码,因此您可以从更好的编译代码中获得效率。
  • 无论如何,25% 的几纳秒仍然不多。
  • 这是一个标准的习惯用法,您可以在 java.util.concurrent 包的许多类中找​​到它 - 例如,参见ThreadPoolExecutor 中的这个方法(其中有很多)
于 2013-06-18T09:52:40.410 回答
0

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

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

所以有两个易失性读取,比一个易失性读取慢。

实际上 JVM 可以将两个 volatile 读取合并为一个 volatile 读取,并且仍然符合 JMM。但是我们希望 JVM 在每次被告知时都会执行善意的 volatile 读取,而不是聪明人并尝试优化掉任何 volatile 读取。考虑这段代码

volatile boolean ready;

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

我们希望 JVM 真正重复加载变量。

于 2013-06-19T02:29:53.003 回答