java - 非最终静态字段的静态初始化安全吗?

标签 java multithreading thread-safety

考虑以下代码:

public class Text {
  private static ThreadLocal<CharsetEncoder> encoderFactory =
    new ThreadLocal<CharsetEncoder>() {
      @Override
      protected CharsetEncoder initialValue() {
        return Charset.forName("UTF-8").newEncoder().
            onMalformedInput(CodingErrorAction.REPORT).
            onUnmappableCharacter(CodingErrorAction.REPORT);
      }
    };

  public static ByteBuffer encode(String string, boolean replace)
      throws CharacterCodingException {
    CharsetEncoder encoder = encoderFactory.get();
    ...
  }
}

encode() 中访问 encoderFactory 的行能否在并发情况下抛出 NullPointerException

是的,我很清楚在这种情况下 encoderFactory 可以很容易地声明为 final,这将使这个问题变得有些无意义。

不过,我在这里感兴趣的是上面写的代码是否仍然安全地发布encoderFactory。如果我理解JLS 12.4 ,应该是这样的。静态初始化的步骤似乎不会让任何线程在看到类已初始化后看到处于未初始化状态的静态字段(即之前没有发生)。我认为 JLS 相当清楚地表明静态初始化形成了内存屏障。

显然已经观察到这样的 NullPointerException,我们最终通过将此字段设置为 final 来修复它。虽然这当然是一件好事,但我仍然很困惑如何用这种模式看到空指针,否则就会出现更大的问题,因为这可能意味着非最终静态字段的任何初始分配都可能不会可见。

如果静态初始化提供内存屏障的假设是可靠的(我相信它是),那么我想这必然指向一个 JDK 错误?除了 JDK 错误之外,您能想到一个可能发生的原因吗?

最佳答案

它将是线程安全的,根据 12.4.2 :

Because the Java programming language is multithreaded, initialization of a class or interface requires careful synchronization.

类的初始化在并发情况下是安全的。这些字段将在任何线程有机会调用 encode 之前加载,无论该字段是否被声明为 final。使用的引用类型没有区别(包括 ThreadLocal)。

他们进一步解释了初始化发生的确切步骤。直到初始化成功或突然完成(通过抛出的异常,导致 ExceptionInInitializerError),线程才会收到通知。

关于java - 非最终静态字段的静态初始化安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36116736/

相关文章:

ruby - ||= 在 Ruby 线程中安全吗?

JavaFX ObservableList 更改不更新 ListView

java - JBoss 5.1.0 GA 中的@EJB?

找不到java android.support.v4.widget.DrawerLayout

java - 重入锁 : Lock/Unlock speed in single-threaded application

c++ - 使用 g++ 删除了 linux 中的 std basic_stringstream 函数

c# - lock(this) 和静态对象上的锁之间的区别

java - 我在使用 java 登录菜单应用程序时遇到问题

c++ - 线程与同步

c++ - boost 等效于 ManualResetEvent?