单例模式

功能

主要作用是保证在Java程序中,某个类只有一个实例存在。一些管理器和控制器常被设计成单例模式。

实现方法

饿汉式

线程安全,无法实现实例懒加载策略。

public class Singleton1 {
    private final static Singleton1 singleton1 = new  Singleton1();
    private Singleton1() {
    }
    public static Singleton1 getInstance() {
        return singleton1;
    }
}

懒汉式

线程不安全,实现了实例懒加载策略。

public class Singleton2 {
    private final static Singleton2 singleton2;
    private Singleton2() {
    }

    public static Singleton2 getInstance() {
        if (singleton2 == null)
            singleton2 = new Singleton2();
        return singleton2;
    }
}

全局锁

线程安全,且实现了懒加载策略,但是线程同步时效率不高。

双重校验锁式

线程安全,且实现了懒加载策略,同时保证了线程同步时的效率。但是volatile强制当前线程每次读操作进行时,保证所有其他的线程的写操作已完成。volatile使得JVM内部的编译器舍弃了编译时优化,对于性能有一定的影响。

静态内部类式【推荐】

外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。即当SingleTon第一次被加载时,并不需要去加载SingleTonHoler,只有当getInstance()方法第一次被调用时,才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。当getInstance()方法被调用时,SingleTonHoler才在SingleTon的运行时常量池里,把符号引用替换为直接引用,这时静态对象INSTANCE也真正被创建。

public class Singleton6 {
    private Singleton6() {
    }

    private static class SingletonHolder {
        private static final Singleton6 INSTANCE = new Singleton6();
    }

    public static Singleton6 getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

枚举方式【推荐】

线程安全,不存在线程同步问题,且单例对象在枚举类型 INSTANCE 第一次引用时通过枚举构造函数 初始化,因而实现了懒加载策略。

这种方式是Effective Java作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象

public class Singleton7 {
    private Singleton7() {
    }

    enum SingletonEnum {
        INSTANCE;

        private final Singleton7 singleton7;

        private SingletonEnum() {
            singleton7 = new Singleton7();
        }
    }

    public static Singleton7 getInstance() {
        return SingletonEnum.INSTANCE.singleton7;
    }

    public static void main(String[] args) {
        IntStream.rangeClosed(0, 100).forEach(i -> new Thread() {
            public void run() {
                out.println(Singleton7.getInstance());
            };
        }.start());
    }
}