我探索了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
呢?没有其他线程可以改变它的风险,它只是为了阅读。
答案 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倍或更高,因此每次调用此方法时获取和释放锁的开销似乎都是不必要的:一旦初始化完成,就会出现获取和释放锁不必要。许多程序员试图以下列方式优化这种情况: