同步惰性初始化时两次检查是否为空的原因是什么?

What is the reason for checking for nullity twice when synchronizing lazy initialization?

我遇到了一个惰性初始化方法,它在应用锁之前和之后两次检查是否为空。代码是这样的,

public void initializeTemplate(){
   if(tmeplate == null){
      synchronized(this){
         if(template == null){
              //initialization is done here!
         }
      }
   }
}

我想知道两次检查无效性的原因是什么?根据我的理解,在应用锁之前进行检查就足够了,对吧?还是我错了?谁能解释一下?

它被称为双重检查锁定This维基百科文章解释的很详细

简而言之,获取锁是一个昂贵的操作,在这个例子中它很可能用于在多线程环境中初始化单例Class。 第一个 if 语句检查对象是否为 null,如果对象不为 null,则无需获取锁以确保没有其他线程初始化该对象,因为该对象已经初始化。但是,如果对象为 null,线程必须获得块上的锁以确保没有其他线程试图初始化对象。

双重检查锁定is considered"broken".

null 检查很便宜,而 synchronized 不是,但是,其他线程可能会在第一个 null 检查和synchronized 块,因此您检查一次(便宜),然后作为预防措施检查一次。

这基本上是在捕获竞争条件,其中两个线程通过外部检查,一个线程将阻塞等待 synchronized 块为其提供锁,而另一个线程通过(已确保锁),创建对象实例并将值赋给 template。当它存在时,等待线程将有机会执行 synchronized 块中的代码,但是您进行第二次 null 检查以确保您没有在做已经完成的工作并打破单实例要求并防止可能的引用问题