[英]Is static initialization of non-final static fields safe?
请考虑以下代码:
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
,我们最终通过使这个字段最终来修复它。 虽然这当然是一件好事,但我仍然感到困惑的是如何看到一个带有这种模式的空指针,否则会出现一个更大的问题,因为它可能意味着非最终静态字段的任何初始分配可能不会是可见的。
如果静态初始化提供内存屏障的假设是可靠的(我相信它是),我想这肯定会指向一个JDK错误呢? 你能想到除了JDK bug之外可能发生的原因吗?
根据12.4.2 ,它将是线程安全的:
由于Java编程语言是多线程的,因此初始化类或接口需要仔细同步。
在并发情况下,类的初始化将是安全的。 在任何线程有机会调用encode
之前,将加载字段,无论字段是否被声明为final。 使用的引用类型没有区别(包括ThreadLocal
)。
他们进一步解释了初始化发生的确切步骤。 在初始化成功或突然完成(通过抛出异常,导致ExceptionInInitializerError
)之前,不会通知线程。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.