Java并发 - 为什么不同步setter(而不是getter)使类的线程安全?

Pab*_*dez 7 java concurrency thread-safety shared-memory

可能重复:
Java类中的线程安全性

在实践中阅读Java并发性,并且我找到了一个令我困惑的例子.

作者声明这个类不是线程安全的

public class MutableInteger {

    private int number;

    public int getInt() {
        return number;
    }

    public void setInt(int val) {
        number = val;
    }
}
Run Code Online (Sandbox Code Playgroud)

并且他们还声明只同步一个方法(例如setter)不会; 你必须同步两者.

我的问题是:为什么?不会同步setter吗?

Mic*_*ker 6

Java在内存模型之前/之后发生.在写路径和读路径上都需要有一些常见的并发构造(例如,同步块/方法,锁,易失性,原子)来触发此行为.

如果同步这两个方法,则会在整个对象上创建一个锁,该锁将由读写线程共享.JVM将确保在离开(synchronized)setInt方法之前在写入线程上发生的任何更改在进入(synchronized)getInt方法后对任何读取线程都是可见的.JVM将插入必要的内存屏障以确保发生这种情况.

如果仅同步write方法,则对任何读取线程可能看不到对象的更改.这是因为JVM可以使用读取路径来确保读取线程的可见内存(缓存等)与写入线程一致.使getInt方法同步将提供.

注意:特别是在这种情况下,使字段'number'为volatile将提供正确的行为,因为volatile读/写也在JVM中提供相同的内存可见性行为,并且setInt方法内部的操作只是一个赋值.


Sou*_*ink 5

在样本之前的书中对此进行了解释(第35页):

"只同步setter是不够的:调用get的线程仍然可以看到陈旧的值."

陈旧数据:当读者线程检查就绪时,它可能会看到过时的值.除非每次访问变量时都使用同步,否则可能会看到该变量的陈旧值.更糟糕的是,陈旧不是全有或全无:线程可以看到一个变量的最新值,但是第一个写入的另一个变量的陈旧值.

  • 我想了解原因。原因是线程保留了变量的本地缓存值,并且读取操作可能不会刷新缓存。如果缓存不存在,则是否有同步方法都没关系(两个操作都是原子的) (2认同)