关于单例模式的N种实现方式

  在开发中常常用到单例模式,单例模式也算是设计模式中最容易理解,也是最容易手写代码的模式,因此也常做为面试题来考。因此想总结一下单例模式的理论知识,方便同窗们面试使用。面试

  单例模式实现的方式只有两种类型,一种是饿汉式(类加载时就初始化)、一种是懒汉式(类加载时不初始化)。饿汉式没什么可讲究的由于它既简单也线程安全,若是条件容许通常咱们都会直接用饿汉式;惟独比较麻烦的是懒汉式,考虑到线程安全,使用懒汉式的单例模式,就有多种实现方式了。设计模式

  1、饿汉式安全

  如上所述,饿汉式很简单,实现方式以下:多线程

public class Singleton{ private static final Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }

  这种单例模式,很简单并且还安全,能够说近乎完美。它惟一的缺陷就是,这种实现方式就没法适用于单例还须要必定的配置或者传参的场景。函数

  2、懒汉式性能

  懒汉式的单例模式,因为要考虑到线程安全的状况,有多种实现方式。优化

  0、以下的实现方式是一种不安全的实现方式this

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

  之因此把定义为编号0,就是除非你很是肯定没有多线程的场景,由于当有多个线程并行调用 getInstance() 的时候,就会建立多个实例。因此绝大部分场景下,这种实现方式都是不该该使用。spa

 

  一、低性能的懒汉式线程

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

  这种实现方式,解决了多线程实例问题,可是因为直接在方法上使用synchronized关键字,即类锁同步,使得只能使用每次调用都要进行同步操做,性能比较低。

 

  二、双重检验锁的懒汉式

public class Singleton { private volatile static Singleton instance; private Singleton() { } public static Singleton getSingleton() { if (instance == null) { // null 检测
      synchronized (Singleton.class) { // 同步检测
        if (instance == null) { instance = new Singleton(); } } } return instance; } }

  咱们看到最多的状况就是没有加volatile的情形,其实你不加这个关键字,面试你的人应该也不会纠结于这个,由于99.99%的状况都是好的(要知道那些提供后台服务的,通常只会保证99%可靠性……)。之因此加是由于JVM存在指令重排的优化,致使

new Singleton() 不是一个原子操做行为,new Singleton() 这句话执行的过程大概分为如下abc步骤:

  a, 给 instance 分配内存;

  b, 调用 Singleton 的构造函数来初始化成员变量;

  c, 将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了);

  JVM 的即时编译器中存在指令重排序的优化,执行顺序多是 abc 也多是 acb。若是是acb,在执行到c,被线程X抢占了,这时 instance 已是非 null 了(但却没有初始化),因此线程X会直接返回 instance,使用一个没有初始化的实例产生错误是必然的。那为何要加volatile呢?通常咱们只用到volatile的可见性功能,即对一个volatile变量的读,老是能看到(任意线程)对这个volatile变量最后的写入,并且常常用于解决同步问题。其实volatile还有一个重要的功能——禁止指令重排优化,也就是volatile标记的变量不会被编译器优化。如上所示,加上volatile之后,读操做必定会发生在abc或者acb以后,而且这个顺序是固定。

 

  三、静态内部类实现懒汉式

public class Singleton { private static class InternalSingleton { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return InternalSingleton.INSTANCE; } }

  因为InternalSingleton 是私有的,除了 getInstance() 以外没有办法访问它,所以它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷。

  四、经过枚举实现懒汉式
  经过枚举写单例超级简单,什么都不用想,这也是它最大的优势。下面这段代码就是声明枚举实例的一般作法。

public enum Singleton { INSTANCE; public void show(int age) { System.out.println("this is show function -> " + age); } }

  咱们能够经过Singleton.INSTANCE来访问实例,这比调用getInstance()方法简单多了。建立枚举默认就是线程安全的,因此不须要担忧double checked locking,并且还能防止反序列化致使从新建立新的对象。可能因为平时咱们使用枚举都是为了表示类别,你们都不多使用这种方式去写单例模式。

相关文章
相关标签/搜索