懒汉式

public class LazySingleton {
    private static LazySingleton singleton;
    private LazySingleton() {}
    public synchronized LazySingleton getInstance() {
        if (singleton == null) {
            singleton = new LazySingleton();
        }
        return singleton;
    }

}

饿汉式

public class HungarySingleton {
    private static HungarySingleton singleton = new HungarySingleton();

    private HungarySingleton(){}

    public static HungarySingleton getInstance(){
        return singleton;
    }
}

双重检查锁

public class DoubleCheckSingleton {

    private static volatile DoubleCheckSingleton singleton;

    private DoubleCheckSingleton() {}

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

使用静态内部类

public class StaticInnerClassSingleton {

    private StaticInnerClassSingleton(){}

    private static class StaticInnerClass{
        private static StaticInnerClassSingleton singleton = new StaticInnerClassSingleton();
    }

    public StaticInnerClassSingleton getInstance(){
        return StaticInnerClass.singleton;
    }
}

使用枚举

public class EnumSingleton {

    private enum  Singleton{
        INSTANCE;
        EnumSingleton enumSingleton;
        Singleton(){
            synchronized (this){
                enumSingleton = new EnumSingleton();
            }
        }
    }
    public EnumSingleton getInstance(){
        return Singleton.INSTANCE.enumSingleton;
    }

}

各种写法的比较

  • 懒汉式:提供了延迟初始化,不使用的时候不会初始化,但是效率不高,每次进来都要加锁,竞争激烈的情况下效率下降严重。
  • 饿汉式:借用了JVM classloader机制,这种方式类加载较慢,但在使用的时候速度会比较快。
  • 枚举:《effective java》中提的方式,简洁,并且无偿提供了序列化机制,绝对的防止了多次实例化,即时是面对各种序列化或反序列化攻击,推荐使用。
  • 静态内部类方式:使用静态内部类的方式,可以借用JVM的classload机制,保证单例,在classloader内部实际上也是通过加锁的方式实现的。
  • 双重检查锁:这个在《java并发编程的艺术》中有提及,如果DoubleCheckSingleton没有被volatile修饰,有可能会产生问题,产生问题的根源在于 singleton = new DoubleCheckSingleton(); 这一行。这个可以理解为如下三行伪代码
memory = allocate();//1、分配对象的内存空间
ctorInstance(memory);//2、初始化对象
instance = memory//3、设置instance指向刚刚分配的内存地址
04-30 18:55