在使用双锁时使单例实例易失性有什么意义?
2022-09-01 01:18:26
private volatile static Singleton uniqueInstance
在单例中使用双锁方法进行同步时,为什么单实例被声明为易失性?我可以在不将其声明为易失性的情况下实现相同的功能吗?
private volatile static Singleton uniqueInstance
在单例中使用双锁方法进行同步时,为什么单实例被声明为易失性?我可以在不将其声明为易失性的情况下实现相同的功能吗?
可防止对内存写入进行重新排序,从而使其他线程无法通过单例的指针读取单例的未初始化字段。volatile
考虑这种情况:线程 A 发现 ,锁定,确认它仍然是 ,并调用单例的构造函数。构造函数在 Singleton 内部写入成员,然后返回。线程 A 现在将对新创建的单例的引用写入 到 中,并准备释放其锁。uniqueInstance == null
null
XYZ
uniqueInstance
就像线程 A 准备释放其锁一样,线程 B 出现了,发现它不是 。线程访问时认为它已被初始化,但由于 CPU 对写入进行了重新排序,因此线程 A 写入的数据尚未对线程 B 可见。因此,线程 B 在 中看到不正确的值,这是错误的。uniqueInstance
null
B
uniqueInstance.XYZ
XYZ
XYZ
标记易失性时,将插入内存屏障。在 之前启动的所有写入都将在修改 之前完成,从而防止上述重新排序情况。uniqueInstance
uniqueInstance
uniqueInstance
如果没有代码,则无法与多个线程一起正常工作。volatile
来自维基百科的双重检查锁定:
从 J2SE 5.0 开始,此问题已得到修复。volatile 关键字现在可确保多个线程正确处理单一实例。这个新习语在“双重检查锁定已损坏”声明中进行了描述:
// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
Helper result = helper;
if (result == null) {
synchronized(this) {
result = helper;
if (result == null) {
helper = result = new Helper();
}
}
}
return result;
}
// other functions and members...
}
通常,如果可能的话,您应该避免双重检查锁定,因为很难正确,如果您弄错了,则可能很难找到错误。请尝试以下更简单的方法:
如果帮助器对象是静态的(每个类装入器一个),则另一种方法是按需初始化持有者习语
// Correct lazy initialization in Java
@ThreadSafe
class Foo {
private static class HelperHolder {
public static Helper helper = new Helper();
}
public static Helper getHelper() {
return HelperHolder.helper;
}
}