第一次写单例模式
public class Singleton {
private static Singleton instance = null;
public static Singleton getInstance() {
if(instance == null) {//1
instance = new Singleton();//2
}
return instance;
}
}
假设现在有两个线程,两个线程都调用getInstance()方法,假设线程A执行完1还没有执行2,此时cpu资源切换给线程B,线程B执行1发现instance为空,执行完new Singleton(),稍后线程A被唤醒,执行new Singleton(),问题出现了,new出了两个实例
将代码改为如下所示
public class Singleton {
private static Singleton instance = null;
public synchronized static Singleton getInstance() {
if(instance == null) {//1
instance = new Singleton();//2
}
return instance;
}
}
这样可以保证线程不出现问题了,但是问题又来了,第一次调用getInstance()初始化instance后每次getInstance()都要进同步方法,性能较为低下,耗时操作都浪费再同步方法上了
正确的做法是只第一次调用getInstance()时进同步方法,往后再调用就直接返回,代码如下
public class Singleton {
private static Singleton instance = null;
public static Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
instance = new Singleton()这里大致做了三件事情
- 给instance实例分配内存
- 初始化instance的构造器
- 将instance对象指向分配的内存空间(注意到这步时instance就非null了)
但JVM为了优化指令,提高程序运行效率,允许指令重排序,因此在程序真正运行时以上指令执行顺序可能是这样的
- 给instance实例分配内存
- 将instance对象指向分配的内存空间(注意到这步时instance就非null了)
- 初始化instance的构造器
这是如果线程A执行到第二步切换到线程B,线程B发现instance不为空,直接取走使用,就报错了
为了解决以上问题,我们可以使用volatile关键字,禁止指令重排序
public class Singleton {
private volatile static Singleton instance = null;
public static Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}