单例模式属于创建型设计模式,单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点
单例性质
-
单例类只能有一个实例。
-
单例类必须自己创建自己的唯一实例。
-
单例类必须给所有其他对象提供这一实例。
常见实现
- 懒汉式,线程不安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 懒汉式,线程安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 饿汉式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
- 双重校验锁
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
双重校验锁 volatitle 的作用
-
禁止指令重排序 :
new Singleton()
是一个非原子操作,编译器可能会重排序,即赋值操作在初始化对象前完成。而线程 B 在线程 A 赋值完时判断 instance 就不为 null 了,此时 B 拿到的将是一个没有初始化完成的半成品。 -
保证可见性:线程 A 在自己的工作线程内创建了实例,但此时还未同步到主存中;此时线程 B 在主存中判断 instance 还是 null ,那么线程 B 又将在自己的工作线程中创建一个实例,这样就创建了多个实例。
Spring中的单例模式的应用
Spring 中加载单例的过程都是在 BeanFactory 接口中定义的 getBean 方法
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//通过名字查找这个单例bean是否存在
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//查看缓存中是否存在这个bean实例
singletonObject = this.earlySingletonObjects.get(beanName);
//如果这个时候的bean实例还是为空并且允许懒加载
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}