设计模式(二)--单例模式

  单例模式是最常见的设计模式,也是项目中或者框架中应用比较广泛的设计模式,今天一起复习的一下单例模式的几种写法。
 
单例模式优点:
  1.保证只有一个实例,所以可以节省内存和计算。
  2.有些场景下,可以保证结果正确。
  3.方便管理。
 
使用场景:
  1.无状态的工具类,例如日期工具类,字符串工具类等。
  2.全局信息类。
 
恶汉模式一:不推荐
public class Singleton {

    private final static Singleton instance = new Singleton();

    private Singleton(){

    }

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

   将实例设置为static,保证只被加载一次,构造器私有化,时间上首先将实例加载,所以是恶汉模式。

恶汉模式二:不推荐

public class Singleton {

    private static final Singleton instance;

    static {
        instance = new Singleton();
    }

    private Singleton(){

    }

    public Singleton getInstance() {
        return instance;
    }
}
  实现和第一种方式几乎完全一样,特点也是一样。
懒汉模式一:不安全
public class Singleton {

    private static Singleton instance;

    private Singleton(){

    }

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

   懒汉模式用时间换内存空间,直到使用的时候才会加载,但是他是线程不安全的,if(instance == null)的判断在多线程场景下同时进入,然后会创建多个实例,那样就不是单例了,所以,需要保证同步,也就是下一种方式。

懒汉模式二:不推荐
public class Singleton {

    private static Singleton instance;

    private Singleton(){

    }

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

   通过synchronized关键字修饰static方法,现在可以保证原子性,同一时间,只有一个线程进入方法,是线程安全的。但是带来的效率问题,只有一个线程进入方法,其他线程都是Blocked状态,所以不推荐使用,有点太粗暴了。

懒汉模式三:不安全

public class Singleton {

    private static Singleton instance;

    private Singleton(){

    }

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

   这样的写法应该一下就发现问题了,虽然用同步块保证同一时间创建实例的时候只有一个线程,但是已经进入了判断,还是会创建多个线程。

双重检查:推荐

public class Singleton {

    //需要volatile修饰,以此保证可见性和有序性
    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;
    }
}

  上一种方法说了会创建多个实例,所以在同步块内部加了非空判断,也就是双重检查,保证只有一个实例。如果只是这样,还忽略一个问题,就是instance = new Singleton();不是一个原子性操作,实际上有三个步骤:

  1.给对象分配内存

  2.调用构造函数初始化成员变量

  3.将对象赋值给instance

  这时候可能发生CPU乱序执行,可能是1-3-2的步骤,只要执行第三部,instance就不是null了,然后其他线程直接返回instance,但此时没有进行初始化,变量都未被赋值的,很有可能出现问题,所以需要用volatile修饰。

优点:

  1.线程安全

  2.延迟加载

  3.效率较高

静态内部类:推荐
public class Singleton {

    private Singleton(){

    }

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

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

   这种也是懒汉模式,只有需要的时候,才会进行初始化,而且静态内部类保证线程安全。

枚举实现:最优

public enum  Singleton {

    INSTANCE;
}

  枚举实现方式,超级简单,调用只需要Singleton.INSTANCE就是实例了,进而进行其他操作处理。Joshua Bloch在《Effective Java》说过:枚举就是实现单例的最佳方式。枚举实例上是final class,继承枚举父类,其方法都是static方法。

优点:
  1.实现简单优雅。
  2.线程安全。
  3.可以避免被反射和反序列化破坏,这是其它方式所没有的。

posted @ 2019-12-12 06:27  Diamond-Shine  阅读(320)  评论(0编辑  收藏  举报