网上盛传单例模式有7种,大致分为懒汉式和饿汉式。其他都是他俩的变种。这里重点分析下二次枷锁的懒汉式单例模式
public class Singleton {
private volatile static Singleton mInstance;//注意点1
private Singleton(){}//注意点2
public static Singleton getInstance(){//注意点3
if(mInstance == null){
synchronized(this){
if(mInstance == null){//注意点4
mInstance = new Singleton();
}
}
}
return mInstance;
}
}
- 注意点1:将mInstance变量定义为volatile。是为了让其他线程可见。volatile的作用是给变量设置内存屏障。当本线程的变量在对此变量进行读写操作时,会锁住CPU总线,完成后会刷新主内存,这样其他线程操作此变量时都是使用的最新的值。保证了变量同步。如果这里没有volatile修饰符,另一个线程可能在变量正在初始化但还未完成时引用到它。使得得到的mInstance实例数据不完整。通过volatile就可以保证happens-before关系,也就是所有对变量的写操作都会在读操作之前发生。个人觉得volatile是为了保证变量完整性才存在的。
- 注意点2:将构造方法定义为private类型,保证外部接口访问不到
- 注意点3:getInstance()为static类型
- 注意点4:双重判空的原因是因为第一次判空后,会对this进行加锁操作,此操作会耗时,如果在枷锁的时候,另外一个线程已经将mInstance实例创建出来了,这儿又创建一个,不符合单例模式只有一个对象的原则。
以上就是对双重加锁单例模式的理解。以下是其他情况,有些没写大同小异
//饿汉式1
static class Singleton1{
private static Singleton1 mInstance = new Singleton1();
private Singleton1(){}
public static Singleton1 getInstance(){
return mInstance;
}
}
//饿汉式2
static class Singleton2{
private static Singleton2 mInstance ;
static{
mInstance = new Singleton2();
}
private Singleton2(){}
public static Singleton2 getInstance(){
return mInstance;
}
}
//静态内部类方式,在被使用的时候才会去加载静态内部类
//懒汉式
static class Singleton3{
private static class SingletonHold {
private static final Singleton3 INSTANCE = new Singleton3();
}
private Singleton3(){}
private static Singleton3 getInstance(){
return SingletonHold.INSTANCE;
}
}
对于上面这种使用静态内部类的方法,我们还能学习到静态内部类的加载时机。和静态变量不大一样,静态变量在整个类加载进来时就已经完成了初始化操作。而对于静态内部类,只有当该类被调用时才回去加载。所以这种也是懒汉式加载