Java并发性-为什么';同步setter(而不是getter)不能使类线程安全吗?
可能重复:Java并发性-为什么';同步setter(而不是getter)不能使类线程安全吗?,java,concurrency,shared-memory,thread-safety,Java,Concurrency,Shared Memory,Thread Safety,可能重复: 我在实践中阅读Java并发性,我遇到了一个让我困惑的例子 作者声明该类不是线程安全的 public class MutableInteger { private int number; public int getInt() { return number; } public void setInt(int val) { number = val; } } 他们还声明只同步一个方法(例如setter)是
我在实践中阅读Java并发性,我遇到了一个让我困惑的例子 作者声明该类不是线程安全的
public class MutableInteger {
private int number;
public int getInt() {
return number;
}
public void setInt(int val) {
number = val;
}
}
他们还声明只同步一个方法(例如setter)是不行的;你必须将两者同步
我的问题是:为什么?同步setter不就是这样吗?Java有一个before/before-after内存模型。在写入路径和读取路径上都需要一些常见的并发构造(例如,同步块/方法、锁、易失性、原子)来触发这种行为 如果同步这两个方法,则将在整个对象上创建一个锁,该锁将由读线程和写线程共享。JVM将确保在离开(同步的)setInt方法之前在写入线程上发生的任何更改,在进入(同步的)getInt方法之后,对任何读取线程都是可见的。JVM将插入必要的内存屏障,以确保实现这一点 如果只同步写入方法,则任何读取线程都可能看不到对对象的更改。这是因为在读取路径上没有JVM可以用来确保读取线程的可见内存(缓存等)与写入线程一致的点。使getInt方法同步将提供这一点
注意:特别是在这种情况下,将字段“number”设置为volatile将给出正确的行为,因为volatile读/写也在JVM中提供相同的内存可见性行为,setInt方法内部的操作只是一个赋值。因为
number
不是volatile,而且getInt()
未同步,getInt()
可能会返回过时的值。有关更多信息,请阅读java内存模型。示例之前的书(第35页)对此进行了解释:
“仅同步setter是不够的:调用get的线程仍然能够看到过时的值。”
陈旧数据:当读卡器线程检查就绪时,它可能会看到一个过期的值。除非每次访问变量时都使用同步,否则可能会看到该变量的过时值。更糟糕的是,过时并不是全部或全无:线程可以看到一个变量的最新值,但可以看到另一个先写入的变量的过时值。如果只同步setter方法,则只能保证属性不会被错误修改,但是,当您尝试读取变量时,无法确定它是否过时 对不起,答案至少是不完整的,而且这个提示对我的眼睛是无用的。他要求对一个具体问题进行解释(-1)这是一个典型的注释行。使字段“number”易变并不会使赋值操作原子化。该方法必须同步如果字段不稳定且该方法未同步,则说明所涉及的实际故障场景。实际上,它有微妙的不同。我正在读这本书,说“它将能够看到过时的值”是不够的,我需要基础。所以,你不相信,如果getter没有同步,并且您想要证明它,那么就有可能看到过时的值,或者您的意思是什么?我想理解为什么。原因是线程保留变量的本地缓存值,读取可能不会刷新缓存。如果缓存不存在,那么是否存在同步方法就无关紧要(两个操作都是原子的)