简体   繁体   English

静态初始化非最终静态字段是否安全?

[英]Is static initialization of non-final static fields safe?

Consider the following code: 请考虑以下代码:

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();
    ...
  }
}

Can the line that accesses encoderFactory in encode() ever throw a NullPointerException in a concurrent situation? 访问encode() encoderFactory的行是否会在并发情况下抛出NullPointerException

Yes, I am well aware that in this case encoderFactory could easily be declared as final which will make this question somewhat moot. 是的,我很清楚,在这种情况下, encoderFactory很容易被声明为final,这将使这个问题有点没有实际意义。

However, my interest here is whether the code written as above still safely publishes encoderFactory . 但是,我感兴趣的是,上面编写的代码是否仍然安全地发布了encoderFactory If I understand the JLS 12.4 , that should be the case. 如果我理解JLS 12.4 ,那应该是这样的。 The steps of static initialization do not seem to leave a possibility that any thread will see static fields in their uninitialized state (ie no happens-before) once it sees the class as initialized. 静态初始化的步骤似乎没有留下任何线程一旦看到类初始化就会看到处于未初始化状态的静态字段(即之前没有发生)的可能性。 I thought that the JLS makes it reasonably clear that static initialization forms a memory barrier. 我认为JLS使得静态初始化形成内存屏障变得相当清楚。

Apparently such a NullPointerException has been observed, and we ended up fixing it by making this field final. 显然已经观察到这样的NullPointerException ,我们最终通过使这个字段最终来修复它。 While it is certainly a good thing to do, I am still puzzled how one can see a null pointer with this pattern, otherwise a much larger problem is afoot, as it could mean that any initial assignment of non-final static fields potentially may not be visible. 虽然这当然是一件好事,但我仍然感到困惑的是如何看到一个带有这种模式的空指针,否则会出现一个更大的问题,因为它可能意味着非最终静态字段的任何初始分配可能不会是可见的。

If the assumption that the static initialization provides a memory barrier is a reliable one (which I believe it is), I suppose that would necessarily point to a JDK bug then? 如果静态初始化提供内存屏障的假设是可靠的(我相信它是),我想这肯定会指向一个JDK错误呢? Can you think of a reason that can happen other than a JDK bug? 你能想到除了JDK bug之外可能发生的原因吗?

It will be thread-safe, as per 12.4.2 : 根据12.4.2 ,它将是线程安全的:

Because the Java programming language is multithreaded, initialization of a class or interface requires careful synchronization. 由于Java编程语言是多线程的,因此初始化类或接口需要仔细同步。

Initialization of a class will be safe in concurrent situations. 在并发情况下,类的初始化将是安全的。 The fields will be loaded before any thread has a chance to invoke encode , whether the field was declared final or not. 在任何线程有机会调用encode之前,将加载字段,无论字段是否被声明为final。 The reference type used does not make a difference (including ThreadLocal ). 使用的引用类型没有区别(包括ThreadLocal )。

They go further in explaining the exact steps in which initialization occurs. 他们进一步解释了初始化发生的确切步骤。 Threads are not notified until initialization has completed successfully or abruptly (via a thrown exception, resulting in an ExceptionInInitializerError ). 在初始化成功或突然完成(通过抛出异常,导致ExceptionInInitializerError )之前,不会通知线程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM