为什么在Facebook YearClass中声明非线程 - 易失性参数

时间:2016-03-05 09:32:31

标签: java multithreading facebook facebook-android-sdk volatile

我探索了Facebook开源device-year-class并发现了一些我想问的有趣内容。

这是一个非常简单的类,可以进行一些计算并返回设备的年份

public class YearClass {  
.
.

      private volatile static Integer mYearCategory;

          public static int get(Context c) {
            if (mYearCategory == null) {
              synchronized(YearClass.class) { 
                if (mYearCategory == null) {
                  mYearCategory = categorizeByYear(c);
                } 
              } 
            } 
            return mYearCategory;
          }

}

为什么他们检查mYearCategory == null条件的两倍以及此变量为什么声明为volatile?它不是从不同的线程初始化的,我们在应用程序生命周期内没有对该值进行更改,我们只是第一次检索它...为什么确保我们从/向内存读取/写入它是如此重要,如果没有volatile会发生什么。也是为什么我们需要synchronized呢?没有其他线程可以改变它的风险,它只是为了阅读。

1 个答案:

答案 0 :(得分:-1)

为什么有静态易变?

如果通过多个线程访问静态值,则每个线程都可以拥有其本地缓存副本。为了避免这种情况,您可以将变量声明为静态volatile,这将强制线程在每次全局值时读取。

现在回答你的第二个问题。为什么有两次检查null?这称为双重检查锁定优化。

什么是双重检查锁定?

          public static int get(Context c) {
            if (mYearCategory == null) {
              synchronized(YearClass.class) { 
                if (mYearCategory == null) {
                  mYearCategory = categorizeByYear(c);
                } 
              } 
            } 
            return mYearCategory;
          }

考虑一下,第一次调用get(Context c)将创建该对象,并且在此期间只有少数尝试访问它的线程需要同步;之后,所有调用只获得对成员变量的引用。由于在某些极端情况下同步方法可能会使性能降低100倍或更高,因此每次调用此方法时获取和释放锁的开销似乎都是不必要的:一旦初始化完成,就会出现获取和释放锁不必要。许多程序员试图以下列方式优化这种情况:

  1. 检查变量是否已初始化(未获得锁定)。如果已初始化,请立即返回。
  2. 获得锁定。
  3. 仔细检查变量是否已经初始化:如果另一个线程首先获得了锁,它可能已经完成了初始化。如果是,则返回初始化变量。
  4. 否则,初始化并返回变量。