茴香豆的九种写法——论java单例写法

懒汉式

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();//一、分配对象的内存空间
ctorInstance(memory);//二、初始化对象
instance = memory//三、设置instance指向刚刚分配的内存地址

在上面3行伪代码2和3之间,可能会被重排序。重排序以后的顺序可能变为 1,3,2。在JVM规范中容许在单线程内,不会改变单线程程序执行结果的重排序。这个重排序在没有改变单线程执行结果的前提下能够提升程序的执行效率。可是这种写法在多线程中会出现问题,if (singleton == null) 进行了判断,可是若是线程T1,限制性了3,这时候instance不等于null,T2进来以后判断singleton!=null,开始拿到这个对象直接使用,可是这个singleton对象尚未被初始化(ctorInstance)。那就是致使T2使用这个对象的时候出错。以上就是若是singleton没有加volatile致使的结果。因为加了volatile,会禁止指令重排序,就不会出现上面的问题。因此这种写法是安全的。java

相关文章
相关标签/搜索