具有内部类的Java Singleton - 什么保证线程安全?

Mal*_*alt 12 java multithreading

一个共同的(1,2)实现单的方式是使用具有静态构件的内部类:

public class Singleton  {    
    private static class SingletonHolder {    
        public static final Singleton instance = new Singleton();
    }    

    public static Singleton getInstance() {    
        return SingletonHolder.instance;    
    }

    private Singleton() {
        //...
    }
}
Run Code Online (Sandbox Code Playgroud)

据说这个实现是懒惰的初始化和线程安全的.但究竟是什么保证了它的线程安全?处理 线程和锁定的JLS 17没有提到静态字段具有任何类型的先发生关系.我怎样才能确定初始化只发生一次并且所有线程都看到同一个实例?

DDo*_*nko 7

它在Java Concurrency in Practice中有很好的描述:

延迟初始化持有者类idiom使用一个类,其唯一目的是初始化Resource.JVM推迟初始化ResourceHolder类,直到实际使用它[JLS 12.4.1],并且因为资源是使用静态初始化程序初始化的,所以不需要额外的同步.任何线程对getresource的第一次调用都会导致ResourceHolder被加载和初始化,此时资源的初始化通过静态初始化器发生.

静态初始化

静态初始化程序在类初始化时由JVM在类加载之后但在任何线程使用该类之前运行.因为JVM在初始化期间获取锁[JLS 12.4.2]并且每个线程至少获取一次此锁以确保已加载该类,所以在静态初始化期间进行的内存写入对所有线程都是自动可见的.因此,静态初始化的对象在构造期间或被引用时不需要显式同步.


Gal*_*man 5

首先我们需要了解两点:

加载类时静态初始化发生一次

在其声明中具有 static 修饰符的字段称为静态字段或类变量。它们与类相关联,而不是与任何对象相关联。类的每个实例共享一个类变量,该变量位于内存中的一个固定位置

....

类的初始化包括执行其静态初始化器和类中声明的静态字段(类变量)的初始化器

这意味着静态初始化器在初始化对象类(实际的Class对象,而不是的实例)时只执行一次。

因为 Java 编程语言是多线程的,类或接口的初始化需要仔细的同步,因为其他线程可能同时尝试初始化相同的类或接口。

对于每个类或接口C,都有一个唯一的初始化锁LC。从CLC的映射由Java 虚拟机实现决定。

现在,简单来说,当两个线程尝试初始化instance第一个获取LC 的线程时,实际上是 initialize instnace,并且因为它是静态执行的,java 提供了它只发生一次的承诺。

有关初始化锁的更多信息,请阅读JSL 17