一、双重校验锁
public class Singleton {
private volatile static Singleton instance=null;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
双重校验锁保证线程安全,并且懒加载,避免一开始就占用内存,过程稍微复杂,Android官方推荐,注意要使用volatile保证从主内存中读取,原因如下:
INSTANCE = new SingleTon();
这个步骤,其实在jvm里面的执行分为三步:
1.在堆内存开辟内存空间。
2.在堆内存中实例化SingleTon里面的各个参数。
3.把对象指向堆内存空间。
由于jvm存在乱序执行功能,所以可能在2还没执行时就先执行了3,如果此时再被切换到线程B上,由于执行了3,INSTANCE 已经非空了,会被直接拿出来用,这样的话,就会出现异常。这个就是著名的DCL失效问题。
二、枚举实现
public enum Singleton{
INSTANCE;
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
枚举实现简单快捷,Effective Java一书中推荐使用枚举实现单例,可以保证效率,而且还能解决反序列化创建新对象的问题,但是Android官方不推荐,因为内存占用会多一些
三、静态内部类
public class InnerStaticMode {
private static class SingleTonHolder {
public static InnerStaticMode sInnerStaticMode = new InnerStaticMode();
}
public static InnerStaticMode getInstance(){
return SingleTonHolder.sInnerStaticMode;
}
}
静态内部类的方式实现单例,可以保证多线程的对象唯一性,还有提升性能,不用同步锁机制,外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存;
但是静态内部类也有着一个致命的缺点,就是传参的问题,由于是静态内部类的形式去创建单例的,故外部无法传递参数进去。