原子 compareAndSet 但有回调?

xen*_*ide 5 java java-8

我知道AtomicReferencecompareAndSet,但我觉得我想做的是这个

private final AtomicReference<Boolean> initialized = new AtomicReference<>( false );
...

atomicRef.compareSetAndDo( false, true, () -> {
  // stuff that only happens if false
});
Run Code Online (Sandbox Code Playgroud)

这可能也有效,可能会更好。

atomicRef.compareAndSet( false, () -> {
  // stuff that only happens if false
  // if I die still false.

   return true;
});
Run Code Online (Sandbox Code Playgroud)

我注意到有一些新的功能结构,但我不确定它们中是否有任何一个是我正在寻找的。

任何新的构造都可以做到这一点吗?如果是这样,请提供一个例子。

更新 为了简化我的问题,我试图找到一种不太容易出错的方法来保护代码,以“为对象做一次”或(真的)懒惰的初始化方式,我知道我团队中的一些开发人员compareAndSet感到困惑。

the*_*472 5

“为对象执行一次”中的保护代码

具体如何实现取决于您希望其他线程同时尝试执行相同的操作。如果你只是让它们运行通过 CAS,它们可能会观察到处于中间状态的事物,而成功的一个线程会执行其操作。

或(真正的)惰性初始化方式

如果您将其用于惰性初始化器,则该构造不是线程安全的,因为“已初始化”布尔值可能会被一个线程设置为 true,然后在另一个线程观察 true 状态但读取空结果时执行该块。

如果可以接受多个并发/重复的初始化尝试,并且一个对象最终获胜而其他对象被 GC 丢弃,则可以使用Atomicreference::updateAndGet 。更新方法应该是无副作用的。

否则,您应该只使用带有可变引用字段的双重检查锁定模式。

当然,您始终可以将其中任何一个打包到一个高阶函数中,该函数返回一个RunnableSupplier,然后将其分配给最终字段。

// ==  FunctionalUtils.java

/** @param mayRunMultipleTimes must be side-effect-free */
public static <T> Supplier<T> instantiateOne(Supplier<T> mayRunMultipleTimes) {
  AtomicReference<T> ref = new AtomicReference<>(null);

  return () -> {
    T val = ref.get(); // fast-path if already initialized
    if(val != null)
      return val;
    return ref.updateAndGet(v -> v == null ? mayRunMultipleTimes.get() : v)
  };

}


// == ClassWithLazyField.java

private final Supplier<Foo> lazyInstanceVal = FunctionalUtils.instantiateOne(() -> new Foo());

public Foo getFoo() {
  lazyInstanceVal.get();
}
Run Code Online (Sandbox Code Playgroud)

您可以通过这种方式轻松封装各种自定义控制流和锁定模式。这是我自己的两个。


JB *_*zet 3

compareAndSet如果更新完成则返回 true,如果实际值不等于预期值则返回 false。

所以只需使用

if (ref.compareAndSet(expectedValue, newValue)) { 
    ... 
}
Run Code Online (Sandbox Code Playgroud)

也就是说,我不太理解您的示例,因为您将 true 和 false 传递给以对象引用作为参数的方法。你的第二个例子并没有做与第一个例子相同的事情。如果第二个是你想要的,我想你追求的是

ref.getAndUpdate(value -> {
    if (value.equals(expectedValue)) {
        return someNewValue(value);
    }
    else {
        return value;
    }
});
Run Code Online (Sandbox Code Playgroud)