Skip to content

单例

单线程环境

public class Singleton {

    private static Singleton instance;

    /**
     * 私有构造
     */
    private Singleton() {
    }

    /**
     * 只适用单线程环境的创建方式
     * @return
     */
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

静态方法,饿汉式

public class SingletonHungry {

    // 利用静态构造只会初始化一次
    private static SingletonHungry instance = new SingletonHungry();

    private SingletonHungry() {
    }

    public static SingletonHungry getInstance() {
        return instance;
    }
}

同步加锁,懒汉式

public class SingletonLazy {

    private static SingletonLazy instance;

    private SingletonLazy(){
    }

    public static synchronized SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

双重校验锁,懒汉式优化

public class SingletonTwice {

    //private static SingletonTwice instance;
    private static volatile SingletonTwice instance;

    private SingletonTwice() {
    }

    /**
     * 减小锁粒度,到真正创建对象的时候才使用同步
     * @return
     */
    public static SingletonTwice getInstance() {
        if (instance == null) {
            synchronized (SingletonTwice.class) {
                if (instance == null) {
                    instance = new SingletonTwice();
                }
            }
        }
        return instance;
    }

}

双检锁在多线程环境下还是可能出现空指针问题。创建对象不是原子操作,分三步: - 分配内存空间 - 初始化对象 - 对象引用指向分配的地址 由于 CPU 可能的优化排序,第三步可能会先于第二步执行,这时其他线程读到就会有问题。解决这个问题只须加上 volatile 关键字,禁止指令重排序即可。

静态内部类


枚举

public class SingletonEnum {

    private SingletonEnum() {
    }

    private static enum Singleton {
        INSTANCE;
        private SingletonEnum singletonEnum;

        private Singleton() {
            singletonEnum = new SingletonEnum();
        }

        public SingletonEnum getInstance() {
            return singletonEnum;
        }
    }

    public static SingletonEnum getInstance() {
        return Singleton.INSTANCE.getInstance();
    }`

}

枚举类型是线程安全的,并且只会装载一次。