0%

GOF23-Singleton

Singleton Pattern

Conceptual

  • 保证一个类只有一个实例

  • 为该实例提供一个全局访问节点

无论何时调用该方法,它总是会返回相同的对象

Pros & Cons

Pros

  • 你可以保证一个类只有一个实例,提供了一个指向该实例的全局访问节点
  • 仅在首次请求单例对象时对其进行初始化

Cons

  • 违反了单一职责原则。 该模式同时解决了两个问题
  • 单例模式可能掩盖不良设计, 比如程序各组件之间相互了解过多等
  • 该模式在多线程环境下需要进行特殊处理, 避免多个线程多次创建单例对象
  • 单例的客户端代码单元测试可能会比较困难, 因为许多测试框架以基于继承的方式创建模拟对象。 由于单例类的构造函数是私有的, 而且绝大部分语言无法重写静态方法, 所以你需要想出仔细考虑模拟单例的方法。 要么干脆不编写测试代码, 或者不使用单例模式

Code

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

private ThreadSafeSingleton(){}

private static volatile ThreadSafeSingleton instance;

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

}

FAQ

不加锁的问题

  • 多线程情况下就不再是单例Object

synchronized直接修饰方法的问题

  • 每次访问方法都会加锁,性能开销大

双重检查锁(double checked locking)

  • 针对对象加synchronized锁(低性能损耗下,返回单例Object)
  • 对象必须加Volatile修饰(处理重排序问题)

Volatile

new 方法编译后实际会出现

  • 分配内存空间
  • 将对象指向刚分配的内存空间
  • 初始化对象

多个操作,即使synchronized已经加了锁,在多线程访问情况下,可能访问获得到的是一个初始化未完成的对象。

因此,添加Volatile关键字禁止重排序,所有的write都发生在read操作前,获取完整对象

Source