设计模式之单例模式
单例模式解释
单例模式是一种对象创建性模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。
单例模式的要点有三个:一是某个类只能有一个实例;而是必须自行创建整个实例;三是它必须自行向整个系统提供整个实例。
英文定义为:Ensure a class only has one instance, and provide a global point of access to it.
单例模式深入分析
单例模式适合一个类只有一个实例的情况, 比如窗口管理器,打印缓冲池和文件系统,它们都是原型的例子。典型的情况是,那些对象的类型被遍及一个软件系统的不同对象访问,因此需要一个全局的访问指针,这便是总所周知的单例模式的应用。当然这只有在你确信你不再需要任何多于一个的实例的情况下。
使用场景及代码实现
饿汉式1——线程安全
/** * 饿汉式 * 在类装载时就创建实例,但是有两个问题: * 1. 如果实例化过程中涉及大量的操作时会导致类装载慢,影响整个装载过程 * 2. 如果创建的对象并没有得到应用,将造成资源的浪费 */ @Slf4j @ThreadSafe public class HungrySingleton1 { // 私有构造方法 private HungrySingleton1() { // 例如以下可能导致类装载过慢 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } log.info("HungrySingleton1"); } // 单例实例 private static HungrySingleton1 instance = new HungrySingleton1(); // 静态的工厂方法 public static HungrySingleton1 getInstance() { return instance; } }
饿汉式2——线程安全
/** * 饿汉式 * 将实例化对象放到静态代码块中 */ @Slf4j @ThreadSafe public class HungrySingleton2 { // 私有构造方法 private HungrySingleton2() { log.info("HungrySingleton2"); } // 单例实例,必须放到静态代码块之前,非实例化出来的对象为null private static HungrySingleton2 instance = null; static { instance = new HungrySingleton2(); } // 静态的工厂方法 public static HungrySingleton2 getInstance() { return instance; } }
懒汉式1——线程非安全
/** * 懒汉式 * 这是线程不安全的,原因是多个线程可能同时判断instance是否为null,然后同时创建对象并返回不同的对象 */ @Slf4j @NotThreadSafe public class LazySingleton1 { // 私有构造方法 private LazySingleton1() { log.info("LazySingleton1"); } // 单例实例 private static LazySingleton1 instance = null; // 静态的工厂方法 public static LazySingleton1 getInstance() { if (null == instance) { instance = new LazySingleton1(); } return instance; } }
懒汉式2——线程安全
/** * 懒汉式 * 这是线程安全的,但是可能造成大量线程的阻塞,不推荐使用这种方式 */ @Slf4j @ThreadSafe @NotRecommend public class LazySingleton2 { private LazySingleton2() { log.info("LazySingleton2"); } private static LazySingleton2 instance = null; public static synchronized LazySingleton2 getInstance() { if (null == instance) { instance = new LazySingleton2(); } return instance; } }
懒汉式3——线程非安全
/** * 懒汉式 * 双重判断同步锁,但是是线程不安全的,分析一下实例化的过程 * 1. memory = allocate() 分配对象的内存空间 * 2. ctorInstance() 初始化对象 * 3. instance = memory 设置instance指向刚分配的内存 * * 但是,此时JVM和CPU优化,发生指令重排,导致上面的执行顺序可能按照1、3、2进行 * 因此,当一个线程按照1、3、2进行了,另一个线程得到的实例可能是一个空指针 */ @Slf4j @NotThreadSafe public class LazySingleton3 { private int i = 1; private LazySingleton3() { log.info("LazySingleton3"); } private static LazySingleton3 instance = null; public static LazySingleton3 getInstance() { if (null == instance) { synchronized(LazySingleton3.class){// 同步锁 if (null == instance) { instance = new LazySingleton3(); } } }else { log.info("{}", instance.i++); } return instance; } }
懒汉式4——线程安全
/** * 懒汉式,推荐使用 * 实例化的过程 * 1. 向内存申请内存空间 * 2. 创建实例 * 3. 将创建的实例指向刚刚申请的内存空间 * * 为了避免JVM指令重排,给实例加上volatile */ @Slf4j @ThreadSafe public class LazySingleton4 { private LazySingleton4() { log.info("LazySingleton4"); } // volatile + 双重判断同步锁 -> 禁止指令重排,实现线程安全 private volatile static LazySingleton4 instance = null; public static LazySingleton4 getInstance() { if (null == instance) { synchronized(LazySingleton4.class){ // 同步锁 if (null == instance) { instance = new LazySingleton4(); } } } return instance; } }