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

时间:2016-12-09 01:31:49

标签: java multithreading synchronization atomic synchronized

考虑以下代码

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();
}

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

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

正如我所看到的,AtomicReference也使用无限循环(又称繁忙等待)来浪费CPU周期来进行更新(参见AtomicReference#getAndUpdate()),所以在这里做同样的方法可能并不是那么糟糕吗?

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

2 个答案:

答案 0 :(得分:3)

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失败的唯一方法是,如果另一个线程在同一时间做了同样的事情。

这称为"重试循环"并且应该只执行很少次(大约一次)。

答案 1 :(得分:2)

AtomicBoolean.getAndSet如果你只想允许一个线程访问一个特定的块,就像你一样,但我不建议在if语句中使用它与其他可能改变的变量,即使这样案件可能是安全的。但是,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();
}