考虑<!>“;对实例字段的延迟初始化的双重检查习惯用法<!>”;

// Item 71 in Effective Java copied from this interview with Bloch.
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;
}

我希望能够以安全的方式重置字段(在我的情况下强制它再次从数据库加载)。我假设我们可以通过重置方法来实现这一点:

void reset() {
   field = null;
}

这是重置字段的标准方法吗?安全吗?任何陷阱?我问,因为Bloch给出了关于双重检查延迟加载的以下警告:<!>“这个成语非常快,但也很复杂和精致,所以不要试图以任何方式修改它。只需复制和粘贴 - 通常不是一个好主意,但在此适当。<!>

提前致谢, 来自喜马拉雅山脉的Playa。

有帮助吗?

解决方案

是的,这是线程安全的。

synchronized块用于防止多个线程不必要地调用computeFieldValue()。由于field是易变的,resetgetField中的访问都是有序的。

如果第一次检查非空,则result完成; <=>被退回。

否则,获取锁定,排除可能将该字段设置为非null的任何其他线程,但允许任何线程将<=>设置为null。如果任何线程将<=>设置为null,则不应该更改任何内容;这是使线程进入同步块的条件。如果另一个线程在当前线程检查后已经获取了锁,并将该字段设置为非空值,则第二次检查将检测到该值。

其他提示

我认为这应该是安全的,但这只是因为你将字段存储在局部变量中。完成此操作后,即使另一个线程正在重置字段的值,也无法将局部变量引用神奇地更改为null。

只要重置方法是上面列出的reset()方法,这似乎就可以工作。但是,如果<=>方法实例化一个新的Object(如下所示),你最终是否可能会返回与你想要的不同的东西?

void reset() {
    field = new FieldType();
}

我想这完全取决于线程安全的含义。

您可能会遇到第二次使用第一个实例的情况。这可能没问题,也可能没有。

我认为reset()方法不正确。如果你阅读第71项,你会发现:

此代码可能看起来有点复杂。特别需要当地人 变量结果可能不清楚。这个变量的作用是确保该字段 在<!>#8217; s已经初始化的常见情况下,只读

延迟初始化不会假设字段可能已更改。如果这些运算符之间的字段将设置为null:

FieldType result = field;
if (result == null) { // First check (no locking)

getField()提供的结果不正确。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top