单例模式 - 大话设计模式

*每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。*

单例模式(Singleton)

保证一个类仅有一个实例, 并提供一个访问的全局访问点[^ DP]

为了不实例化出多个对象,让类自身负责保存他的唯一实例. 这个类可以保证没有其他实例可以被创建, 并且他可以提供一个访问该实例的方法

单例模式

单例模式

单例模式因为Singleton类封装了他唯一的实现类, 可以严格控制客户怎样访问以及何时访问它.—对唯一实例的受控访问

示例代码:

java

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {
private static Singleton instance; // private的实例变量

private Singleton() {
System.out.println("you win!!!");
} // 私有的构造方法 让其他类不能访问

public static Singleton getInstance() {
if (instance == null) instance = new Singleton(); // 如果实例为空则创建
return instance;
}

多线程单例模式

在多个线程中, 单例模式会出现多个线程同时访问Singleton类, 会出现创建了多个实例的情况. 所以我们给他加

双重锁定

当然加锁后每个线程想访问Singleton类都要等待了, 所以我们优化一下 — 双重锁定

java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Singleton {
private static Singleton intance; // private的实例变量
private static Object synchronizedLOCK = new Object(); // 锁对象
//只有static的成员才能在没有创建对象时进行初始化。且类的静态成员在类第一次被使用时初始化后就不会再被初始化,保证了单例

private Singleton() {
System.out.println("you win!!!");
} // 私有的构造方法 让其他类不能访问

public static Singleton getIntance() {
if (intance == null) {
synchronized (synchronizedLOCK) {
if (intance == null) {
intance = new Singleton();
} // 如果实例为空则创建
}
}
return intance;
}

饿汉/懒汉单例类

  • 饿汉

    1. 静态初始化的方式在自己被加载时就将自己实例化

    java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class SingletonHungry {
    private static SingletonHungry intance = new SingletonHungry();

    public SingletonHungry() {
    System.out.println("Creat new !!!");
    }

    public static SingletonHungry getIntance() {
    return intance;
    }
    }
  • 懒汉

    1. 在第一次被引用时,才会将自己实例化
  • 优缺点: 饿汉类一加载就实例出对象, 会提前占用系统资源. 懒汉面临多线程访问的单圈问题,要加双重锁定. 具体用哪个取决于实际需求

  • 具体用哪个取决于实际需求

懒汉下防止指令重排序

创建实例的方式intance = new Singleton(); 这不是一个原子性的操作

执行实例化时有三步的

  1. 分配内存空间
  2. 执行构造方法,初始化对象
  3. 将对象指向分配的空间

如果指令2和3重排 那么创建的就是空对象…

java

1
private volatile static Singleton intance; // 加入volatile防止重排

防止反射破坏单例模式

使用 一个加密的 开关参数(可以破译)

使用枚举 反射源码不允许创建枚举的实例 枚举是一个继承了Enum的类