多线程常用单例模式

一、双重校验锁

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,故而不占内存;
但是静态内部类也有着一个致命的缺点,就是传参的问题,由于是静态内部类的形式去创建单例的,故外部无法传递参数进去。

参考资料1:Java单例模式的正确实现
参考资料2:深入理解单例模式:静态内部类单例原理

tag(s): Java Android 
show comments · back · home
0评论