【问题标题】:Lazy initialization using single check idiom使用单检查习语进行延迟初始化
【发布时间】:2012-12-20 21:43:32
【问题描述】:

在“Effective Java, Second Edition”的第 71 项中,引入了双重检查习惯用法和单一检查习惯用法,用于延迟实例化实例字段。

双重检查习语

private volatile FieldType field;
FieldType getField() {
  FieldType result = field;
  if (result == null) {
    synchronized(this) {
      result == field;
      if (result == null)
        field = result = computeFieldValue();
    }
  }
  return result;
}

单次检查习语

private volatile FieldType field;
FieldType getField() {
  FieldType result = field;
  if (result == null) {
    field = result = computeFieldValue();
  }
  return result;
}

在复查习语 Joshua 中,result 变量用于确保 volatile field 只读取一次,从而提高性能。这我理解,但是我不明白为什么我们需要在单检查习语中使用它,因为无论如何我们只读取一次 field

【问题讨论】:

  • 双重检查已过时,因为存在多处理器环境:ibm.com/developerworks/java/library/j-dcl/index.html
  • @alfasin:这篇 IBM 文章已过时。使用 Java 5 内存模型和对 volatile 字段的保证,上述双重检查习惯用法是可以的。
  • 内存模型不是这里唯一的问题,从“JIT 编译器会在这里看到优化机会......”开始阅读
  • @alfasin:这篇文章来自 2002 年。十一年前。从那以后情况发生了变化。你不认为乔什·布洛赫知道他在说什么吗?
  • @JBNizet 这是一种争论的方式吗?我应该指望别人说/写的吗?就个人而言,我喜欢了解事情背后的原因,而不是“依靠某人的话”,即使是大师......

标签: java volatile lazy-initialization effective-java


【解决方案1】:

在单检查习语中,如果没有结果变量,您仍然会阅读两次;一次用于空检查,一次用于返回值。

【讨论】:

    【解决方案2】:

    我更喜欢以下惰性求值的实现:

    @ThreadSafe
    class MyClass {
        private static class MyClassHelper {
           public static final MyClass helper = new MyClass();
        }
    
        public static MyClass getInstance() {
            return MyClassHelper.helper;
        }
    }
    

    【讨论】:

    • 这是懒初始化房间的正确方法。虽然其他方法可以达到相同的效果,但在这里您依靠 Classloader 的优雅解决方案。虽然我会让辅助字段最终化。
    • 它没有编译,因为他使用“HelperHolder.helper”而不是“MyClassHelper.holder”,只是一个小改动/错字。
    • @JBNizet 我把它写在记事本上...修复了。谢谢贾里德! :)
    • 没什么,静态字段没问题。只是想提一下,因为我最初谈论的是实例字段的惰性求值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-21
    • 1970-01-01
    • 2020-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多