一、对象的创建过程
视频教程
创建->初始化->建立连接
1.先申请内存,赋值默认值0
2.构造方法赋值初始值,8
三、建立连接,t->T
二、DCL单例
public class Singleton { private static Singleton instance = null; public static Singleton getInstance() { if(null == instance) { // line A instance = new Singleton(); // line B } return instance; } }
假设这样的场景:
并发调用两个线程Singleton.getInstance()假设线程先判断instance是否为null,即代码中line A进入到line B位置。判断后,JVM将CPU向线程二切换资源,
由于线程1尚未执行line B,所以instance还是空的,所以线程二执行了new Singleton()操作。过了一会儿,当线程再次被唤醒时,它仍然执行new Singleton()操作,
这样来了,new出了两个instance,这还能叫单例吗?
public class Singleton { private static Singleton instance = null; public synchronized static Singleton getInstance() { if(null == instance) { instance = new Singleton(); } return instance; } }
与第一段代码相比,方法中只有一个synchronized现在可以保证不会出线程问题。
但是这里有一个很大的性能问题(至少耗时很大)。
。
public class Singleton { private static Singleton instance = null; public static Singleton getInstance() { synchronized (Singleton.class) { if(null == instance) { instance = new Singleton(); } } return instance; } }
基本上,把synchronized移动到代码内部是没有意义的。每次调用getInstance()还是要进行同步。
同步本身没有问题,但我们只想第一次创建它instance例子同步,所以有以下写法——
public class Singleton { private static Singleton instance = null; public static Singleton getInstance() { if(null == instance) { // 检测到线程二instance不为空 synchronized (Singleton.class) { if(null == instance) { instance = new Singleton(); // 线程一被指示重排,首先执行执行,但结构函数(即初始化未完成)尚未完成。 } } } return instance; // 执行后面的线程2将导致对象尚未初始化错误 } }
除了第一次创建对象外,其他访问似乎已经满足了要求。if回来,所以不会走到同步块,完美吗?
如上代码段注释:假设线程一执行到instance = new Singleton()这句话在这里看起来像一句话,但实际上是编译后的JVM执行对应的会变代码发现,这句话被编译成8个汇编指令,大致做了三件事:
1)给instance实例分配内存;
2)初始化instance的构造器;
3)将instance对象指向分配的内存空间(注意到这步时instance就非null了)
。这样,当程序真正运行时,上述指令执行顺序可能是这样的:
a)给instance实例分配内存;
b)将instance对象指向分配的内存空间;
c)初始化instance的构造器;
此时,当线程一执行时b)完成,执行c)此时,它被切换到线程2instance判断为非空,线程二直接来retrn instance语句,拿走instance然后使用,接着就顺理成章地报错()。
。
根据以上分析可知,。
public class Singleton {
private volatile static Singleton instance = null;
public static Singleton getInstance() {
if(null == instance) {
synchronized (Singleton.class) {
if(null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}
将变量instance使用volatile修饰即可实现单例模式的线程安全。
参考博客、视频教程