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。