我知道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
永远不会为空的情况下(而是引用&#34; null object&#34;懒惰地替换为&#34;非空对象的值#&# 34;),this
上的同步是否可以细化为synchronized(field)
?
我有一个非常大的HugeObject
,但很容易重新制作。我希望垃圾收集器能够在内存开始耗尽的情况下丢弃此实例,因此我使用Reference<HugeObject>
保留它,并将其初始化为Reference<HugeObject> field = new SoftReference<>(null)
。我希望避免锁定整个对象this
,以便即使在初始化field
期间也可以使用不会影响field
状态的方法。那么它是否可以简单地获取 这个初始&#34; null对象的锁定&#34; 或已经实例化的&#34;非空对象&#34;,或者这是否会导致细微的,不需要的并发效果?请参阅以下代码:
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;
}
答案 0 :(得分:4)
如果您不想在this
上进行同步,则可以使用其他参考。但是这个参考需要保持不变。在您的示例中,您将锁定field
并重新分配 - 因此,如果field
具有不同的值,则两个线程可以同时执行您的方法。
一种标准解决方案是使用专用锁:
private final Object lock = new Object();
//...
synchronized(lock) { ... }
答案 1 :(得分:0)
您绝对不想锁定null可变成员。大多数人锁定this
或班级。但是,如果您不想这样做,assylias的解决方案适用于您的用例。您还可以查看java.util.concurrent.locks
包。它提供了在不使用synchronized
块的情况下手动锁定的方法。也许ReentrantLock
会对你有用吗?该软件包还有一些很酷的东西,比如条件变量和旋转锁(可能不是你在这里使用的东西)。另外,我写了一篇关于单例模式和双重检查锁定的使用以及可能对你感兴趣的“持有者”模式的文章。 https://medium.com/@michael.andrews/deconstructing-the-singleton-b5f881f85f5