单线程下的单例模式:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton()
}
return instance;
}
}
几个关键点:
- static 修饰:表名属于类而不是类对象,不会每生成一个新的类对象都新生成一份。并且可以在不创建类对象的情况下直接调用。
- 为什么构造函数是
private
类型?不然呢,开放了构造函数还怎么单例。 - 为什么不把单例的逻辑放到构造函数中?在
Singleton()
中调用Singleton()
么,那不是死循环了。 - 类中的单例变量是
private
类型的,不能直接访问,要通过getInstance()
来获取。
多线程下的单例模式:
和单线程有什么区别?
- 需要考虑线程安全问题
- 需要考虑效率问题
方法一:
只需要给 getInstance
方法添加 synchronized
关键字即可。
public static synchronized Singleton getInstance() {
问题:每次访问都要同步,会降低性能。
方法二:
双重检查锁定
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton()
}
}
}
return instance;
}
}
关键点:
- 性能优化:将
synchronized
放到实际创建时,只有第一次实例未创建才会同步,后续都不会。 - 为什么要双重检查
instance==null
?第一次检查完,有可能被别的线程先创建了。 - 为什么
instance
要用volatile
修饰?- 因为
new Singleton()
不是一个单一的操作,会存在指令重排的问题。 - 1、为
instance
分配内存空间。2、初始化instance
。3、将instance
指向分配的内存地址。 - 如果指令重拍后,变为了 1-3-2,那么其他线程可能会拿到一个还没初始化的
instance
。
- 因为
- 为什么有了
volatile
还需要synchronized
?- 因为
volatile
不保证原子性。
- 因为