Android单例模式中的双重判空

2017-07-26  本文已影响86人  Gunter1993
public class ApiManager  {
    private static volatile ApiManager mInstance; 

    private ApiManager() {
    }

    public static ApiManager getInstance() {
        if (mInstance == null) {   // 判空1
            synchronized (ApiManager.class) { 
                if (mInstance == null) {   // 判空2
                    mInstance = new ApiManager();
                }
            }
        }
        return mInstance;
    }

}

mInstance为什么要加volatile?

假设线程A执行了mInstance = new ApiManager(),但没有及时刷新到主内存中,并且恰好在解锁后,线程B刚好进来执行锁区域内的if (mInstance == null) ,由于线程A没有把实例化后的mInstance刷新到主内存中,所以此时 mInstance == null为true,于是导致了多次实例化。而加了volatile可以让变量具备线程可见性,即保证:子线程读取到volatile变量时会立刻从主内存中取最新的值,子线程给volatile变量赋值后及时更新至主内存。(详情可参考《volatile及指令重排序》

为什么要进行两次判空?

1、假如没有判空1,只有判空2:
降低了多线程下的执行效率。比方说:mInstance不为空,且线程A和线程B同时需要执行getInstance(),那么每次如果线程A持有锁,那么线程B就得进入阻塞状态,等线程A判空完毕后线程B才能继续执行,这不就降低了性能。有了判空1,那么当mInstance不为空时,线程A和线程B只需一步判空并且不会发生阻塞。

2、假如没有判空2,只有判空1:
多线程执行getInstance()有可能导致多次实例化。因为假设线程A执行了判空1后,线程B也开始执行判空1,此时线程A和线程B都满足了mInstance == null的条件,即使线程A实例化mInstance,也无法影响线程B实例化mInstance。

上一篇下一篇

猜你喜欢

热点阅读