3

是否保证每个线程都看到非最终字段的实例初始化器的值(字段等号的表达式)?例如:

class Foo {
  private boolean initialized = false; // Initializer
  private final Lock lock = new ReentrantLock();

  public void initialize() {
    lock.lock()
    try {
      // Is initialized always false for the first call of initialize()?
      if (initialized) {
        throw new IllegalStateException("already initialized");
      }
      // ...
      initialized = true;
    } finally {
      lock.unlock();
    }
  }
}
4

4 回答 4

4

在那种特定情况下,您很好,因为false它也是booleanfields的默认值。如果您的实例变量初始化是:

private boolean initialized = true;

那么您将无法保证线程会读取true.

请注意,如果该字段是静态的,由于类加载语义,您将有这样的保证。

参考:JLS 17.4.4(重点是我的)

将默认值(零、假或空)写入每个变量与每个线程中的第一个操作同步。
尽管在分配包含变量的对象之前将默认值写入变量似乎有点奇怪,但从概念上讲,每个对象都是在程序开始时使用其默认初始化值创建的

于 2013-06-07T09:28:07.553 回答
0

这同样适用于引用字段的初始值设定项:

如果你想让其他线程看到它的当前值,你必须使用volatile.

volatile然而,这不是万无一失的:大多数情况下,您必须使用synchronized或其他同步方式才能确定,但​​在这种情况下,avolatile就足够了。

请参考这个volatile关于线程安全的用法和问题的问题。

另一件事是只有一个线程可以构造对象,并且在构造对象时会发生实例初始化。但是,您必须小心不要让this引用从构造函数中逃逸。

于 2013-06-07T09:23:48.027 回答
0

在我看来,您正在寻找的是一种线程安全的延迟初始化方式。由于直接使用诸如此类的低级类ReentrantLock可能很难正确执行,因此我建议使用双重检查习语:

private volatile FieldType field = null;    // volatile!

public FieldType getField() {
    FieldType result = field;   // read volatile field only once, after init
    if (result == null) {
        synchronized(this) {
            result = field;
            if (result == null) {
                result = computeFieldValue();
                field = result;
            }
        }
    }
    return result;
}

请注意,双重检查锁定至少需要 Java 1.5。在旧版本的 Java 上,它被破坏了。

于 2013-06-07T09:25:50.790 回答
0

单独的非最终字段不能保证被正确看到,除非您有一些其他保护,例如锁或同步块。即在这种情况下,由于使用值的方式,它总是正确的。

顺便说一句:为简单起见,我建议您始终构建代码,以便在构造函数中初始化组件。这避免了检查对象未初始化或初始化两次等问题。

于 2013-06-07T10:18:16.000 回答