单例模式(Singleton Pattern)这种设计模式属于创建型设计模式 ,在《设计模式》中对单例模式定义是:保证一个类仅有一个实例,并提供一个访问他的全局访问点。本文章会带你一步一步推导出双检锁
创建单例模式原型
这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
public class SingletonLH {
private static SingletonLH instance;
private SingletonLH (){}
public static SingletonLH getInstance() {
if (instance == null) { //在执行这条语句时会有多个线程进入 会被实例化多次
instance = new SingletonLH();
}
return instance;
}
}
改进1
public class SingletonLH {
private static SingletonLH instance;
private SingletonLH (){}
//每次获取对象都要获取同步操作 性能影响大
public static synchronized SingletonLH getInstance() { //<--
if (instance == null) {
instance = new SingletonLH();
}
return instance;
}
}
改进2
可以在只有在创建实例的时候加锁,但又会有一个问题(看下面注释)
public class SingletonLH {
private static SingletonLH instance;
private SingletonLH (){}
public static SingletonLH getInstance() {
if (instance == null) { //如果多个线程运行到这里 当第一个线程释放锁后又会有其他线程进入 导致重复创建对象
synchronized(SingletonLH.class){
instance = new SingletonLH();
}
}
return instance;
}
}
改进3 双检锁雏形
public class SingletonLH {
private static SingletonLH instance;
private SingletonLH (){}
public static SingletonLH getInstance() {
if (instance == null) { //第一个判断
synchronized(SingletonLH.class){
if (instance == null) {//如果已经创建对象 下一个线程将不会进来
instance = new SingletonLH();
}
}
}
return instance;
}
}
改进4 双检锁
成员变量 new SingletonLH()
1、分配空间申请内存
2、初始化对象然后调用构造方法
3、对象指向内存地址
如果调用顺序是1,3,2 这就是指令重排,把分配空间指向变量,但是还没有调用调用构造方法,如果这个时候调用这个方法,instance 是不为null的,但是对象成员变量是没有初始化的。使用volicate关键字会禁止指令重排,避免这种错误
public class SingletonLH {
private volatile static SingletonLH instance; //<--加入关键字 防止指令重排
private SingletonLH (){}
public static SingletonLH getInstance() {
if (instance == null) {
synchronized(SingletonLH.class){
if (instance == null) { // 违背了happens-before原则
instance = new SingletonLH();
}
}
}
return instance;
}
}
改进5 利用内部类
静态内部类在程序启动的时候不会被加载 之后被第1次调用的时候才会被加载
public class SingletonLH {
private static class SingletonLHolder{
private static final SingletonLH INSTANCE = new SingletonLH();
}
private SingletonLH (){}
public static final SingletonLH getInstance() {}
return SingletonLHolder.INSTANCE;
}
}
The End ! ! !