单例模式空值检查疑问:Google示例中双重校验的原因解析
为什么单例实现中要做两次
sInstance == null的空值检查? 这是**双重检查锁定(Double-Checked Locking)**的经典实现,咱们一步步说清楚为啥要两次检查:
第一次锁外检查:性能优化
当单例实例已经创建完成后,每次调用getInstance()都能直接跳过同步块,直接返回实例。如果没有这次检查,不管实例有没有创建,每次调用都得进入同步块等待锁,这在高并发场景下会带来很大的性能开销——毕竟锁的获取和释放是有成本的。第二次锁内检查:保证线程安全
假设没有这次检查,当多个线程同时通过了第一次null检查(此时实例还没创建),它们会排队等待进入同步块。第一个线程进入后创建了实例,后面的线程拿到锁后,还是会执行buildDatabase()再次创建实例,这就彻底破坏了单例的唯一性。锁内的第二次检查能确保,只有第一个进入同步块的线程会创建实例,后续线程进来时看到实例已经存在,直接返回即可。
咱们结合你贴的代码再看就更清楚了:
public static AppDatabase getInstance(final Context context, final AppExecutors executors) { if (sInstance == null) { // 第一次检查:快速返回已创建的实例,避免不必要的锁操作 synchronized (AppDatabase.class) { if (sInstance == null) { // 第二次检查:确保只有第一个线程能创建实例 sInstance = buildDatabase(context.getApplicationContext(), executors); sInstance.updateDatabaseCreated(context.getApplicationContext()); } } } return sInstance; }
另外补充个关键点:在Java中,为了避免指令重排序导致线程拿到未完全初始化的实例,sInstance变量需要被声明为volatile,不然双重检查锁定的逻辑可能会出现隐蔽的bug哦。
内容的提问来源于stack exchange,提问作者justanoob
相关产品推荐
相关产品推荐

