为什么这个静态最终变量在单例线程安全中?

时间:2012-01-14 20:21:48

标签: java multithreading

阅读本网站,我发现了this

  

[行] private static final Foo INSTANCE = new Foo();仅在实际使用类时执行,它负责延迟实例化,并保证线程安全。

为什么这保证是线程安全的?因为此字段最终?或者出于其他原因?

4 个答案:

答案 0 :(得分:25)

因为它是最终的,是的。最终变量具有特殊的线程安全语义,因为其他线程保证至少在其构造函数完成时看到最终字段。

这是JLS 17.5,虽然语言有点密集。这些语义是在Java 1.5中引入的,特别是JSR-133。有关JSR-133的非规范讨论及其各种含义,请参阅此页。

请注意,如果您在构造函数之后修改实例,那么 not 必然是线程安全的。在这种情况下,您必须采取通常的线程安全预防措施,以确保发生在边缘之前。

我非常肯定(虽然不是100%)只有一个线程进行类初始化的事实是不是这里的一个因素。确实这个类只是由一个线程初始化,但是我不相信在该线程之间建立任何其他使用该类的线程的任何特定的发生前边缘(除了那个其他线程不需要重新初始化)班级)。因此,如果没有final关键字,另一个线程将能够看到对象的部分构造的实例。 JMM定义的特定发生前边缘位于JLS 17.4.5中,并且未在此处列出类初始化。

答案 1 :(得分:5)

类构造函数和静态/实例初始化程序保证以原子方式执行,因为private static final FOO INSTANCE = new FOO;等同于

private static final FOO INSTANCE;

static{
    INSTANCE = new FOO();
}

这种情况属于上述类别。

答案 2 :(得分:3)

保证是线程安全的,因为JVM保证在单个线程上执行静态初始化器。

这并不意味着Foo的实例在内部是线程安全的 - 它只是意味着你可以保证在一个线程上通过这个特定的代码路径只调用一次Foo的构造函数。

答案 3 :(得分:2)

任何类的静态初始化块都保证是单线程的。更简单的单例是使用枚举

enum Singleton {
    INSTANCE;
}

这也是线程安全且类惰性初始化。