设计模式(1)——单例模式

单例模式(Singleton Pattern)保证一个类仅有一个实例,并提供一个访问它的全局访问点

饿汉式

饿汉式基于 classloader 机制避免了多线程的同步问题,会在第一次调用时就直接初始化,线程安全,但是可能会浪费空间。

可以在使用的时候再创建对象,于是出现了懒汉式。

1
2
3
4
5
6
7
8
9
10
11
public class SingletonGreed {

public SingletonGreed() {
}

private final static SingletonGreed SINGLETON = new SingletonGreed();

public static SingletonGreed getInstance() {
return SINGLETON;
}
}

懒汉式

这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式,并发下会出现重复创建对象的问题,线程不安全

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SingletonLazy {

public SingletonLazy() {
}

private static SingletonLazy SINGLETON;

public static SingletonLazy getInstance() {
if (SINGLETON == null) {
SINGLETON = new SingletonLazy();
}
return SINGLETON;
}

双重检测锁

这里给懒汉式引入了双重检测锁(DCL, Double Checked Locking),做到线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SingletonLazy {

public SingletonLazy() {
}

private volatile static SingletonLazy SINGLETON;

public static SingletonLazy getInstance() {
if (SINGLETON == null) {
synchronized (SingletonLazy.class) {
if (SINGLETON == null) {
SINGLETON = new SingletonLazy();
}
}
}
return SINGLETON;
}
}

其中由于 SINGLETON = new SingletonLazy(); 不是原子性操作,分为以下三步:

  1. 分配内存空间
  2. 执行构造函数,初始化对象
  3. 把对象指向这个空间
    所以我们需要加上 volatile 关键字,防止指令重排。

反射破坏单例

懒汉式还有个问题是会被反射破坏掉单例状态,如下演示:

1
2
3
4
5
6
7
SingletonLazy instance1 = SingletonLazy.getInstance();
Constructor<SingletonLazy> declaredConstructor = SingletonLazy.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
SingletonLazy instance2 = declaredConstructor.newInstance();

System.out.println(instance1);
System.out.println(instance2);

静态内部类

静态内部类可以实现和双重检测锁一样线程安全的效果,实现起来更简单。

和上面的一样,这个也是不安全的,会被反射破坏单例。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SingletonHolder {

private SingletonHolder() {
}

public static class InnerClass {
private static final SingletonHolder HOLDER = new SingletonHolder();
}

public static SingletonHolder getInstance() {
return InnerClass.HOLDER;
}
}

枚举

枚举可以很好的防止反射,同样也是线程安全的。

1
2
3
4
5
6
7
8
9
public enum SingletonEnum {

INSTANCE;

public SingletonEnum getInstance() {
return INSTANCE;
}

}

反射测试

1
2
3
4
5
6
7
SingletonEnum instance1 = SingletonEnum.INSTANCE;
Constructor<SingletonEnum> declaredConstructor = SingletonEnum.class.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);
SingletonEnum instance2 = declaredConstructor.newInstance();

System.out.println(instance1);
System.out.println(instance2);


设计模式(1)——单例模式
https://slw.im/2021/09/design-pattern-singleton/
作者
Ryo Shen
发布于
2021年9月20日
许可协议