本文共 3162 字,大约阅读时间需要 10 分钟。
public class Singleton { private static Singleton sSingleton; private Singleton() { } public static Singleton getInstance() { if (sSingleton == null) { sSingleton = new Singleton(); // line A } return sSingleton; }}上述做法好像没啥问题,由于mSingleton是静态的,因此能够保证程序运行过程中只存在一个实例。但是针对多线程情况,就可能有问题,比如有2个线程同时并发调用getInstance方法,并且同时执行到了line A,这个时候还是会各自new一个对象出来,也就是说,存在了两个实例,这违背了单例模式的概念,下面我们改进一下:
public class Singleton { private static Singleton sSingleton; private Singleton() { } public static Singleton getInstance() { synchronized (Singleton.class) { if (mSingleton == null) { sSingleton = new Singleton(); } return sSingleton; } }}上述做法的确是没啥问题了,getInstance方法中对Singleton.class加锁,可以保证同一时刻只有一个线程能够进入getInstance方法。现在考虑一种情况,还是我们的比较庞大的工程,在某个变态的时刻,我们需要访问Singleton对象100次,注意是高并发下的同时访问,会是什么情形呢?大概是这样的:100个线程进入getInstance方法以后开始抢mSingleton的所有权,这个时候,有一个线程获得了锁,然后顺利地得到了Singleton实例,接着会是什么情形呢?应该是这样的:剩下99个线程开始抢 mSingleton的所有权,一直这样类推下去,可能有一个线程运气比较差,抢了100次才抢到锁,程序的表现可能是这样的:这个运气差的线程被阻塞在getInstance方法中,迟迟无法返回,如果需要返回数据给ui的话,那么ui将迟迟不会得到更新。
public class Singleton { private volatile static Singleton sSingleton; private Singleton() { } public static Singleton getInstance() { if (sSingleton == null) { // line A synchronized (Singleton.class) { // line C if (sSingleton == null) sSingleton = new Singleton(); // line B } } return sSingleton; }}上述代码近乎完美,可以满足几乎所有场合(采用反射和类加载器另当别论)。上述代码的好处在于:第一次创建实例的时候会同步所有线程,以后有线程再想获取Singleton的实例就不需要进行同步,直接返回实例即可。还有double-check的意义在于:假设现在有2个线程A和B同时进入了getInstance方法,线程A执行到line A行,线程B执行到line B行,由于B线程还没有初始化完毕,sSingleton还是null,于是线程A通过了sSingleton==null的判断,并且往下执行,碰巧,当线程A执行到line C的时候,线程B初始化完毕了,然后线程B返回,注意,如果没有double-check,这个时候线程A就执行到了line B,就会再次初始化 sSingleton,这个时候Singleton实际上被new了两次,已经不算完全意义上的单例了,而有了double-check,就会再进行一次为null的判断,由于B线程已经初始化了sSingleton,所以A线程就不会再次初始化sSingleton。
public class Singleton{ private Singleton(){ } private static class InstanceHolder{ private static final Singleton instance = new Singleton(); } public static Singleton getInstance(){ return InstanceHolder.instance; }}就目前来看,DCL和静态内部类单例模式是高并发场合首选的单例实现方式,在一些对并发要求不高的场合,我们也可以采用其他简单的写法,要做到具体情况具体分析,选择适合的单例模式也是很有必要的,而不是一味地去追求高并发。
转载地址:http://meysi.baihongyu.com/