【问题标题】:Double-checked locking of non-null class member field非空类成员字段的双重检查锁定
【发布时间】:2018-01-10 22:53:57
【问题描述】:

我知道Java 的经典double-checked locking 习惯用法,它首先检查给定字段是否为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 上的同步可以细化为@ 987654329@?

示例

我有一个非常大的HugeObject,但可以很容易地重新创建。我希望垃圾收集器能够在内存开始用完的情况下丢弃这个实例,所以我用Reference&lt;HugeObject&gt; 保存它,并将它初始化为Reference&lt;HugeObject&gt; field = new SoftReference&lt;&gt;(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;
}

【问题讨论】:

  • 用于同步块的监视器不是恒定的 - 这看起来不正确。在this(或private final Object lock - new Object();)上同步有什么问题?
  • 我宁愿避免锁定整个对象this,这样即使在field 的初始化期间也可以调用不影响field 状态的方法。但是,我从没想过使用锁/互斥对象;这可能是我会接受的答案。
  • 我想强调的是,在整个对象this 上进行同步不会影响“不影响field 状态的方法”,即调用这些方法只会阻塞它们包含synchronized(this);见stackoverflow.com/questions/11981227/…

标签: java synchronization member weak-references double-checked-locking


【解决方案1】:

如果您不想在this 上同步,您可以使用其他参考。但该参考需要保持不变。在您的示例中,您锁定了重新分配的 field - 因此如果两个线程具有不同的 field 值,则它们可以同时执行您的方法。

一个标准的解决方案是使用专用锁:

private final Object lock = new Object();

//...

synchronized(lock) { ... }

【讨论】:

    【解决方案2】:

    您绝对不想锁定一个空的可变成员。大多数人锁定this 或班级。但是,如果您不想这样做,那么 assylias 的解决方案非常适合您的用例。您还可以查看java.util.concurrent.locks 包。它提供了在不使用synchronized 块的情况下手动锁定的方法。也许ReentrantLock 对你有用?这个包还有一些很酷的东西,比如条件变量和自旋锁(可能不是你在这里使用的)。另外,我写了一篇关于单例模式和双重检查锁定的使用以及您可能感兴趣的“持有人”模式的文章。 https://medium.com/@michael.andrews/deconstructing-the-singleton-b5f881f85f5

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-26
      • 1970-01-01
      • 2011-08-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多