使用 Atomic 和无限循环在 JAVA 中进行同步

Using Atomic and infinite loop to do synchronisation in JAVA

考虑以下代码

static AtomicBoolean initialized = new AtomicBoolean(false);
static AtomicBoolean initStarted= new AtomicBoolean(false);

public static void init() {
    if (!initialized.get() && !initStarted.getAndSet(true)) {
        doInitialization();
        initialized.set(true);
    }
    // start waiting
    while (!initialized.get());
    // finished waiting
    doMoreStuff();
}

它实现了我想要确保在 doInitialization() 完成之前不调用 doMoreStuff() 并且只有第一个线程应该调用 doInitialization().

我的问题是,这与对整个 init() 方法使用 synchronized 块相比如何?

我看到 AtomicReference 也使用无限循环(a.k.a 忙等待)浪费 CPU 周期来做更新(见 AtomicReference#getAndUpdate()),所以可能还不错在这里作为同步方法做同样的事情?

如果无限循环如此糟糕(例如浪费 CPU 循环)那么为什么 AtomicReference 不使用 synchronized 来停止或唤醒线程?

AtomicReference#getAndUpdate 未使用忙等待阻塞,直到外部条件发生变化。

134        * Atomically sets to the given value and returns the old value.
135        *
136        * @param newValue the new value
137        * @return the previous value
138        */
139       public final V getAndSet(V newValue) {
140           while (true) {
141               V x = get();
142               if (compareAndSet(x, newValue))
143                   return x;
144           }
145       }

循环预期 运行 只有一次,除非在争用的情况下。 compareAndSet 失败的唯一方法是另一个线程在同一时间做同样的事情。

这称为 "retry-loop",应该只执行很少的次数(大约一次)。

AtomicBoolean.getAndSet 如果您只想允许单个线程访问特定块,就像您所做的那样,但我不建议在 if-statement 中与其他变量一起使用它可能会改变,即使这种情况可能是安全的。然而,你的 while 循环在等待时消耗了 100% CPU,所以我建议你改用 CountDownLatch。

AtomicBoolean initialized = new AtomicBoolean(false);
CountDownLatch lock = new CountDownLatch(1);

public void init() throws InterruptedException {
    if (!initialized.getAndSet(true)) {
        doInitialization();
        lock.countDown();
    }
    // start waiting
    lock.await();
    // finished waiting
    doMoreStuff();
}