设计模式之单例模式

写在最前面

单例的文章很多,所以本文主要想记录下几种常用单例的不好理解之处

  • 双重检测 静态资源上的volatile 关键字作用 ?
  • 静态内部类怎么实现线程安全单例 ?
  • 枚举单例怎么理解 ?

单例的概念

  • 单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的一个类只有一个实例。即一个类只有一个对象实例。

  • Java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”
    详情见百度百科

单例的特点

  • 一是某个类只能有一个实例
  • 二是它必须自行创建这个实例
  • 三是它必须自行向整个系统提供这个实例
    详情见百度百科

单例的几种写法

  • 饿汉模式 (线程安全 低效)
/**
 * 类加载时就创建实例
 */
public class Singleton {
	private Singleton(){}
    private final static Singleton singleton= new Singleton();
    public static Singleton getInstance(){
        return singleton;
    }
}
/**
 * 静态代码块实现(类加载时)
 */
public class Singleton {
	private Singleton() {}
    private static Singleton singleton;
    static {
        singleton= new Singleton();
    }
    public static Singleton getInstance() {
        return singleton;
    }
}
  • 懒汉模式 (线程不安全)
 public class Singleton {
 	private Singleton(){}
    private static Singleton singleton;
    public static Singleton getInstance() {
        if (null == singleton) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

  • 懒汉加锁 (线程安全 低效)
/**
 * 方法加锁
 */
public class Singleton {
	private Singleton() {}
    private static Singleton singleton;
    public static synchronized Singleton getInstance() {
        if (null == singleton) {
            singleton = new Singleton();
        }
        return singleton;
    }
}
/**
 * 代码块加锁
 */
public class Singleton {
	private Singleton() {}
    private static Singleton singleton;
    public static Singleton getInstance() {
        if (null == singleton) {
            synchronized (Singleton.class) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}
  • 双重检测 (线程安全 高效)

      创建实例代码       singleton = new Singleton();
      主要做了三件事     (1.开辟空间   2.创建对象  3.地址指向)
      1>  不加volatile:可能执行顺序为132 这时第二个获取资源会是地址不为空但是对象实例为空
      2>  加上volatile:防止JVM指令重排 确保执行顺序为123 确保要么静态变量为NULL 要么有地址指向并且对象实例也不为空
    
public class Singleton {
	private Singleton() {}
    private static volatile Singleton singleton;
    public static Singleton getInstance() {
        if (null == singleton) {
            synchronized (Singleton.class) {
                if (null == singleton) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
  • 静态内部类 (线程安全 高效)

      静态内部类是怎么保证线程安全的呢? 
      虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有
      一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类的
      <clinit>()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()
      方法后,其他线程唤醒之后不会再次进入<clinit>()方法。同一个加载器下,一个类型只会初始化一次。)
      在实际应用中,这种阻塞往往是很隐蔽的
    
public class Singleton {
    private Singleton() {}
    private static class SingletonInner {
        private static final Singleton singleton= new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonInner.singleton;
    }
}
  • 枚举 (线程安全 高效)

     枚举实现单例可能不太好理解
     其实在编译成class文件时,会默认为枚举类成员加上 final static修饰
     再想想就和静态内部类的实现几乎一样了
     在实际代码中可以将需要执行一次的可写在私有方法中
    
public enum Singleton {
    INSTANCE;
    public void methodA() {
    }
    // 枚举中私有方法只会执行一次
    private void methodB() {
    }
}

参考文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章