静态内部类加载
//静态内部类加载
class Singleton {
private static Singleton singleton;
private Singleton() {
}
private static class SingletonInner {
private static final Singleton instance = new Singleton();
}
public static Singleton getSingleton() {
return SingletonInner.instance;
}
}
静态内部类不会在单例加载时加载,当调用 getSingleton() 方法时才会进行加载,达到类似懒汉式效果,并且也是线程安全的。
类的静态属性只会在第一次加载类时进行初始化,所以上面的方法JVM 帮助我们保证了线程的安全性,在类进行初始化时,其他线程无法进入。
JVM在类的初始化阶段(即在class被加载后,且被线程使用之前),会执行类的初始化。在执行类的初始化期间,JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化。
枚举
//枚举方法
enum Singleton {
INSTANCE;
public void method() {
}
}
//调用
class Text {
public static void main(String[] args) {
Singleton.INSTANCE.method();
}
}
// ————————————————
public class User {
//私有化构造函数
private User(){ }
//定义一个静态枚举类
static enum SingletonEnum{
//创建一个枚举对象,该对象天生为单例
INSTANCE;
private User user;
//私有化枚举的构造函数
private SingletonEnum(){
user=new User();
}
public User getInstnce(){
return user;
}
}
//对外暴露一个获取User对象的静态方法
public static User getInstance(){
return SingletonEnum.INSTANCE.getInstnce();
}
}
自由串行化;保证只有一个实例;线程安全。(TODO 枚举类保证),而且可以避免反射破坏Effective Java 作者所提倡的方法,近乎完美,在继承场景下不适用。
懒汉式双重检查终极版
//懒汉式双重检查终极版(推荐)volatile
class Singleton {
private static volatile Singleton singleton;//volatile可以禁止重排序,synchronized不行,因此去掉这句还是可能会因重排序导致线程不安全
private Singleton() {
}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();//这是关键
}
}
}
return singleton;
}
}
此方法给singleton 的声明上加了关键字 volatile ,进而解决了低概率的线程不安全问题,如果去掉是不对的。volatile 起到禁止指令重排的作用,在它赋值完成之前,就不会调用读操作(singleton == null)。
//instance = new Instance();分解
memory=allocate(); //1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
instance = memory; //3:设置instance指向刚分配的内存地址
因为new操作可以被分解为如上三个操作,如果这三个操作重排序为132,其他线程就有可能拿到未经初始化的实例。所以去掉volatile会使得线程不安全。