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

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

atomicRef.compareSetAndDo( false, true, () -> {
  // stuff that only happens if false
});

这可能也会工作,可能会更好。
atomicRef.compareAndSet( false, () -> {
  // stuff that only happens if false
  // if I die still false.

   return true;
});

我注意到有一些新的功能构造,但是我不确定它们是否是我要寻找的。

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

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

最佳答案



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



如果您将其用于惰性初始化程序,则该构造不是线程安全的,因为一个线程可能将“已初始化” boolean 值设置为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();
}

您可以通过这种方式轻松地封装各种自定义控制流和锁定模式。 Here are two of my own.

10-06 05:14