单例模式的实现

提前初始化

静态初始化

Singleton类加载过程中静态初始化阶段初始化单例对象,单例对象被安全发布。执行类加载前会先获取锁,保证同一时间只有一个线程执行Singleton的类加载操作。每个线程在使用Singleton类之前都至少获取一次这个锁以确保这个类已经加载

public class Singleton {
    public static Singleton singleton = new Singleton();

    private Singleton() {}

    public static Singleton getSingleton() {
        return singleton;
    }
}

延迟初始化

监视器锁

利用监视器锁确保单例对象的初始化只会由某一线程完成,缺点是单例对象初始化后获取单例对象的常规执行路径仍然需要加锁,影响性能

public class Singleton {
    public static Singleton singleton;

    private Singleton() {}

    public synchronized static Singleton getSingleton() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

双重检查加锁

单例对象初始化后获取单例对象的常规执行路径不再需要加锁

静态变量singleton必须使用volatile修饰,否则对象发布为不安全发布,在getSingleton方法中第一次判断singleton对象是否为null时,可能会观测到一个未完整初始化的对象,并最终返回这个对象

public class Singleton {
    public static volatile Singleton singleton;

    private Singleton() {}

    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

占位内部类

延迟初始化最理想的方式,原理与提前初始化中的静态初始化一致,加入占位内部类可以让静态初始化延迟

public class Singleton {

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

    private Singleton() {}

    public static Singleton getSingleton() {
        return SingletonHolder.singleton;
    }
}

参考文档

  • Brain Goetz等,Java并发编程实战(Java Concurrency in Practice),机械工业出版社,2012:284-286