11

关于内存可见性的小问题。

代码示例 1:

class CustomLock {

    private boolean locked = false;

    public boolean lock() {
        if(!locked) {
            locked = true;
            return true;
        }
        return false;
    }
}

此代码在多线程环境中容易出现错误,首先是因为“if-then-act”不是原子的,其次是因为潜在的内存可见性问题,例如 threadA 将字段设置为 true,但 threadB后来希望读取该字段的值可能看不到,但仍然看到该值是假的。

最简单的解决方案是使用 synchronized 关键字,如 CodeSample2。

代码示例2:

class CustomLock {

    private boolean locked = false;

    public synchronized boolean lock() {
        if(!locked) {
            locked = true;
            return true;
        }
        return false;
    }
}

现在,如果我希望使用原子变量,例如 AtomicBoolean(问题适用于所有原子变量),该怎么办?

代码示例 3:

   public static class CustomLock {
    private AtomicBoolean locked = new AtomicBoolean(false);

    public boolean lock() {
        return locked.compareAndSet(false, true);
    }
}

除了更好的性能考虑之外,我们可以看到现在我们已经使用 AtomicBoolean 实现了与CodeSample1中的“if-then-act”类似的逻辑。代码在逻辑上做什么并不重要,我的问题是,如果 2 个线程几乎同时调用CodeSample3中的 lock() 方法会怎样,而很明显,现在对该字段的任何写入操作都将以原子方式完成,使用 AtomicBoolean 是否也保证了内存可见性?

对不起,长篇大论,只是想确保我尽可能清楚地表达出来,谢谢大家...

4

2 回答 2

12

是的,根据它保证的javadocs

compareAndSet 和所有其他读取和更新操作(例如 getAndIncrement)具有读取和写入 volatile 变量的记忆效应。

于 2017-03-17T22:45:16.883 回答
3

我的问题是,如果 2 个线程几乎同时调用 CodeSample3 中的 lock() 方法会怎样,虽然很明显,现在对该字段的任何写操作都将以原子方式完成,但使用 AtomicBoolean 是否也能保证内存可见性?

为了AtomicBoolean同时处理来自不同线程的多个操作,它必须保证内存可见性。它可以保证,因为它包装了一个volatile字段。它的语言语义volatile确保跨越内存障碍,以便多个线程看到最新的值,并且任何更新都将发布到主内存。

顺便说一句,您的lock(...)方法应该准备好了,tryLock(...)因为它可能无法获得锁定。

于 2017-03-29T01:24:40.343 回答