解释
确保一个类只有一个实例,并且自己实例化唯一的一个对象
场景
当一个对象没有成员变量时候(无状态对象),可以通过单例模式创建
当一个对象需要产生较多资源开销的时候,可以用单例模式创建一个对象,让他永久驻留内存
最简单的单例模式,可能存在并发问题
public class Singleton {
private static Singleton singleton = null;
//构造函数设置为私有的,限制通过构造函数创建对象
private Singleton() {
}
public static Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
我们来分析一下再多线程情况下可能存在的问题
//线程A
public static Singleton getInstance(){
1 if(singleton==null){ //cup 1
2 singleton = new Singleton();// cup 3
3 }
4 return singleton;
}
//线程B
public static Singleton getInstance(){
1 if(singleton==null){// cup 2
2 singleton = new Singleton();//cup 4
3 }
4 return singleton;
}
cup 1、2…是指线程获得cup时间片,两段代码是属于同一个方法
当线程A执行到行数1时候,表达式返回结果为true
这个时候线程B获得到cup资源,也执行到行数1,表达式返回结果也为true
然后线程A和线程B会分别创建两个对象,违背了单例的初衷
单例模式经典双重校验
public class Singleton {
private static volatile Singleton singleton = null;
private Singleton() {
}
public static Singleton getInstance(){
1 if(singleton==null){
2 synchronized(Singleton.class) {
3 if(singleton==null) {
4 singleton = new Singleton();
}
}
}
return singleton;
}
}
synchronized为何不放在方法上
如果 使用 synchronized Singleton getInstance(),那么每个线程调用getInstance时候都要争抢锁资源
为什么要加两层if判断
1.当线程A判断第一个if(行1)时候返回true就会去争抢锁资源,这个时候抢到锁资源,但是还没执行4。
2.这个时候线程B去读取第一个if(行1) 是,因为线程A的行4还没执行,所以也返回true。
3.线程A执行完行4,并且释放锁资源,线程B获取锁资源,判断第二if(行3),这个时候因为线程A已经把对象Singleton赋值给引用singleton,第二个if(行3)返回false,就不会在进入行4重复创建对象
为什么要加volatile限定词
因为 singleton = new Singleton();不是一个原子操作。分为三个操作指令
1.给Singleton分配内存。
2.初始化Singleton对象。
3.把对象Singleton赋值给引用singleton。
java执行程序会对指令进行重排序,线程A存在先执行3再进行2的情况,那么就有可能导致未完全初始化的对象(只执行步骤3)赋值给引用singleton,当线程B在第一个if(行1)判断时返回true,而此时拿到的对象是个半成品
volatile的作用是,Java线程内存模型确保所有线程看到这个变量的值是一致的,同时还会禁止指令重排序