设计模式~单例模式

目录
一、概述
二、饿汉式
三、懒汉式
3.1 基本实现:
3.2 指令重排

一、概述 什么是单例模式:在整个系统中,每个类仅有一个对象实例
应用场景:

  • 业务系统中仅需要一个对象的时候,redis连接对象
  • Spring IOC容器中的对象默认是单例的
单例模式的分类:
  • 饿汉式:在加载类的时候就创建对象;
  • 懒汉式:懒加载模式,在需要获取对象的时候才会创建对象
单例模式实现步骤:
  • 构造方法私有化
  • 私有的实例对象
  • 对外暴露获取实例对象的方法
二、饿汉式 饿汉式实现简单,而且不会涉及线程安全问题;对象在类加载的时候就会创建,但是缺点在于如果对象不被使用,就会一直占据系统资源
public class Singleton {// 饿汉式 private static Singleton singleton = new Singleton(); private Singleton() { System.out.println("生成一个实例"); }public static Singleton getInstance() {return singleton; } }

调用:
public static void main(String[] args) { new Singleton(); // 编译无法通过 Singleton tareget = Singleton.getInstance(); }

三、懒汉式 实现方面,就是什么时候要获取实例对象,什么时候进行创建; 注意:懒汉式有线程安全问题
3.1 基本实现:
/** *懒汉式实现 */ public class SingletonLazy {// 1.私有成员变量,但不直接创建 private SingletonLazy singletonLazy; // 2.构造函数私有化 private SingletonLazy() { System.out.println("创建一个对象"); }// 3.对外暴露获取对象的方法 public SingletonLazy getInstance() { if (singletonLazy == null) { singletonLazy = new SingletonLazy(); } return singletonLazy; } }

这个实现中,可能存在同时多个线程调用方法,导致最终创建的对象不是同一个,可以使用synchronized解决,但是考虑如果对整个方法上锁,整个执行效率会产生下降,因此应该考虑局部上锁。
而且,这个锁一定上在 if 中,否则和对整个方法上锁没什么区别
// 3.对外暴露获取对象的方法 public SingletonLazy getInstance() { if (singletonLazy == null) {// 考虑这个部分是否有问题??synchronized (SingletonLazy.class) { singletonLazy = new SingletonLazy(); } } return singletonLazy; }

看注释的部分,可能存在一个场景:两个线程A, B同时进入 if 中,但是A先获取锁并创建对象,而B在获取锁之后又重新创建了对象,对象发生了变化;
因此,存在一次锁内检查:
// 3.对外暴露获取对象的方法 public SingletonLazy getInstance() { if (singletonLazy == null) {synchronized (SingletonLazy.class) { if (singletonLazy == null) { singletonLazy = new SingletonLazy(); } } } return singletonLazy; }

结论:双重检测锁定
3.2 指令重排 考虑对象的创建过程:
  • 分配空间给对象
  • 初始化对象
  • 设置对象的内存地址,此时instance != null
但是在JVM中可能由于优化的原因导致创建对象的指令发生混乱:如 1->2-> 3 变为 1 -> 3 -> 2
产生的现象:
当第一个线程拿到锁并且进入到第二个if方法后, 先分配对象内存空间, 然后再instance指向刚刚分配的内存地址, instance 已经不等于null, 但此时instance还没有初始化完成。如果这个时候又有一个线程来调用getInstance方法, 在第一个if的判断结果就为false, 于是直接返回还没有初始化完成的instance, 那么就很有可能产生异常。
而使用 volatile 加载实例对象上,可是保证 变量线程间可见以及禁止指令重排
懒汉式完整实现:
public class SingletonLazy {// 1.私有成员变量,但不直接创建 private volatile SingletonLazy singletonLazy; // 2.构造函数私有化 private SingletonLazy() { System.out.println("创建一个对象"); }// 3.对外暴露获取对象的方法 public SingletonLazy getInstance() { if (singletonLazy == null) {synchronized (SingletonLazy.class) { if (singletonLazy == null) { singletonLazy = new SingletonLazy(); } } } return singletonLazy; } }

【设计模式~单例模式】

    推荐阅读