我知道经典double-checked locking成语Java ,它首先检查给定字段是否为 null
如果是这样,则获取具有以下字段的类的锁:
// Double-check idiom for lazy initialization of instance fields
// See Pascal Thivent's original answer <https://stackoverflow.com/a/3580658/1391325>
private volatile FieldType field;
FieldType getField() {
FieldType result = field;
if (result == null) { // First check (no locking)
synchronized(this) {
result = field;
if (result == null) { // Second check (with locking)
field = result = computeFieldValue();
}
}
}
return result;
}
但是,在 field
的情况下永远不会为空(而是引用“null object”值,该值被延迟替换为“非空对象”),可以同步this
细化为synchronized(field)
?
示例
我有一个非常大的 HugeObject
然而,它可以很容易地重新创建。我希望垃圾收集器能够在内存开始耗尽的情况下丢弃此实例,因此我用 Reference<HugeObject>
保留它。 ,并将其初始化为Reference<HugeObject> field = new SoftReference<>(null)
。我宁愿避免锁定整个对象 this
这样使用的方法就不会影响 field
的状态即使在 field
初始化期间也可以调用。那么是否可以简单地获取这个初始“空对象”或已经实例化的“非空对象”上的锁,或者这可能会导致微妙的结果,不需要的并发影响?请参阅下面的代码:
private volatile Reference<HugeObject> field = new SoftReference<>(null);
HugeObject getField() {
HugeObject result = field.get();
if (result == null) {
synchronized(field) {
result = field.get();
if (result == null) {
result = computeFieldValue();
field = new SoftReference<>(result);
}
}
}
return result;
}
最佳答案
如果您不想在此
上进行同步,则可以使用其他引用。但该引用需要保持不变。在您的示例中,您锁定了被重新分配的 field
- 因此,如果两个线程具有不同的 field
值,则它们可以同时执行您的方法。
一个标准解决方案是使用专用锁:
private final Object lock = new Object();
//...
synchronized(lock) { ... }
关于java - 非空类成员字段的双重检查锁定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45482437/