【设计模式】单例模式

单例模式的几种写法

1、懒汉式,非线程安全

private修饰构造方法使得从外部无法直接创建实例(反射除外),但是没有考虑多线程环境。

public class Singleton {
    //懒汉式-非线程安全
    private static Singleton instance;
    private Singleton() {

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

2、懒汉式,线程安全

加了一个synchronized就可以实现线程安全版本,但是效率太低

public class Singleton {
    //懒汉式-线程安全
    private static Singleton instance;
    private Singleton() {

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

3、饿汉式

利用类加载机制在加载类时就创建了实例,但是没有达到lazy-loading的效果。

public class Singleton {
    //饿汉式
    private static Singleton instance = new Singleton();
    private Singleton() {

    }
    public static Singleton getInstance () {
        return instance
    }
}

变种

public class Singleton {
    //饿汉式
    private static Singleton instance;
    static {
        instance = new Singleton();
    }
    private Singleton() {

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

4、静态内部类

静态内部类不会在外部类加载时加载,而是在第一次被调用时加载,所以这种方法也是可以达到延迟加载的。

public class Singleton {
    //饿汉式-静态内部类
    private static Singleton instance;

    private static class Holder {
        private static final Singleton instance = new Singleton();
    }
    private Singleton() {

    }
    public static Singleton getInstance () {
        return Holder.instance;
    }
}

5、枚举

枚举还不是很理解。

public enum Singleton{
	INSTANCE;
}

6、双重检查锁

还有一个方法就是大名鼎鼎的双重检查锁,可以同时达到线程安全和延迟加载两个要求。

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }

        return instance;
    }
}

开始我不太明白为什么要加volatile关键字修饰instance,后面看到这篇文章:

为什么双重检查锁要加volatile

原因是创建对象的过程也不是原子性的,

instance = new Singleton();

memory = allocate();   //1:分配对象的内存空间
instance = memory;     //3:设置instance指向刚分配的内存地址
                   //注意,此时对象还没有被初始化!
ctorInstance(memory);  //2:初始化对象

所以仍有可能被编译器重排序导致多线程环境下创建出多个实例来,而加了volatile之后可以禁止编译器重排序,从而使得创建对象的过程可以在正确的顺序下完成。

原文地址:https://www.cnblogs.com/puyangsky/p/6502112.html