为什么在 Facebook YearClass 中声明非线程 - volatile 参数
Why declaring non thread - volatile parameter in Facebook YearClass
我探索了 Facebook 开源 device-year-class,发现了一些有趣的问题,我想问一下。
这很简单 class 做一些计算和 returns 你设备的年份
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
?它不是从不同的线程初始化的,我们在应用程序生命周期中没有对该值进行更改,我们只是第一次检索它......为什么确保我们 read/write 它 [=23] 如此重要=] 内存,如果没有 volatile
会怎样。还有为什么要synchronized
呢?没有其他线程可以更改它的风险,它只是为了阅读。
为什么有static volatile变量?
如果您通过多个线程访问静态值,则每个线程都可以有其本地缓存副本。为避免这种情况,您可以将变量声明为 static 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 倍或更高,因此每次调用此方法时获取和释放锁的开销似乎是不必要的:一旦初始化完成,获取和释放锁就会出现不必要。许多程序员尝试通过以下方式优化这种情况:
- 检查变量是否已初始化(未获取锁)。如果它被初始化,return它立即。
- 获取锁。
- 仔细检查变量是否已经初始化:如果另一个线程首先获得了锁,它可能已经完成了初始化。如果是这样,return 初始化变量。
- 否则,初始化并 return 变量。
我探索了 Facebook 开源 device-year-class,发现了一些有趣的问题,我想问一下。
这很简单 class 做一些计算和 returns 你设备的年份
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
?它不是从不同的线程初始化的,我们在应用程序生命周期中没有对该值进行更改,我们只是第一次检索它......为什么确保我们 read/write 它 [=23] 如此重要=] 内存,如果没有 volatile
会怎样。还有为什么要synchronized
呢?没有其他线程可以更改它的风险,它只是为了阅读。
为什么有static volatile变量?
如果您通过多个线程访问静态值,则每个线程都可以有其本地缓存副本。为避免这种情况,您可以将变量声明为 static 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 倍或更高,因此每次调用此方法时获取和释放锁的开销似乎是不必要的:一旦初始化完成,获取和释放锁就会出现不必要。许多程序员尝试通过以下方式优化这种情况:
- 检查变量是否已初始化(未获取锁)。如果它被初始化,return它立即。
- 获取锁。
- 仔细检查变量是否已经初始化:如果另一个线程首先获得了锁,它可能已经完成了初始化。如果是这样,return 初始化变量。
- 否则,初始化并 return 变量。