Java 如果getter被标记为synchronized,为什么此代码会结束?
当方法Java 如果getter被标记为synchronized,为什么此代码会结束?,java,multithreading,synchronized,non-volatile,Java,Multithreading,Synchronized,Non Volatile,当方法get()被标记为已同步时,尽管字段值不是易失性的,为什么此代码成功完成?如果没有同步,它将在我的机器上无限期运行(如预期的那样) 同步强制this.value=value发生在get()之前 它确保更新值的可见性 没有同步,就没有这样的保证。它可能工作,也可能不工作。对于初学者来说,值需要是易失性的,或者获取和设置都需要是同步的,这才是正确的 JLS 17.4.5: 监视器上的解锁发生在监视器上的每个后续锁定之前 在释放锁之前,value可以设置为5,这会将其置于发生之前边缘之前,并使其
get()
被标记为已同步时,尽管字段值不是易失性的,为什么此代码成功完成?如果没有同步,它将在我的机器上无限期运行(如预期的那样)
同步强制
this.value=value
发生在get()
之前
它确保更新值的可见性
没有同步,就没有这样的保证。它可能工作,也可能不工作。对于初学者来说,
值
需要是易失性的
,或者获取
和设置
都需要是同步的
,这才是正确的
JLS 17.4.5:
监视器上的解锁发生在监视器上的每个后续锁定之前
在释放锁之前,value
可以设置为5
,这会将其置于发生之前边缘之前,并使其在下次获取锁时可用
应该注意的是,这种保证是脆弱的,并且取决于线程调度程序,可能根本不存在。在同步模型较弱的平台上,可能看不到与此处相同的效果
另见:
安迪·特纳部分正确 在
get()
方法上添加synchronized
会影响内存可见性要求,并导致(JIT)编译器生成不同的代码
但是,严格地说,在连接set(…)
调用和get()
调用之前需要有一个发生关系。这意味着set
方法应该是synchronized
以及get
(如果要这样做的话!)
简言之,您观察到的代码版本不能保证在所有平台和所有情况下都能工作。事实上,你很幸运
从字里行间看,您似乎在试图通过实验了解Java的内存模型是如何工作的这不是一个好主意。问题在于,您正试图对一个极其复杂的黑匣子进行反向工程,但没有足够的“输入参数”1供您更改以涵盖黑匣子行为的所有潜在方面 因此,“通过实验学习”的方法很容易让你产生不完整或错误的理解 如果您想全面准确地理解,应该从阅读一本好的教科书中有关Java内存模型的内容开始。。。或者JLS本身。无论如何,使用实验来确认您的理解,但是您确实需要知道JMM只指定(保证)如果您做正确的事情会发生什么。如果你做了错误的事情,你的代码可能仍然有效。。。取决于各种因素。因此,通常很难从实验上确认某一特定的做事方式是正确的还是不正确的2 1-一些您需要的参数实际上并不存在。例如,一个允许您运行Java N For N>12,或者一个允许您在您无权访问的硬件上运行。。。或者那还不存在
2-如您的示例所示。您得到的答案是“正确的”,即使代码是错误的。如果没有正确的同步,程序结果是不确定的。程序可以正确运行和完成,也可以不正确,这完全取决于JVM实现的突发奇想(该实现可能随时发生变化)。这是一个非常详细和正确的解释,应该标记为正确答案。感谢您提供了完整的答案@Stephen!我知道这段代码是坏的(无论是否在getter上同步),我绝对不会在生产中使用类似的东西。
public class MtApp {
private int value;
/*synchronized*/ int get() {
return value;
}
void set(int value) {
this.value = value;
}
public static void main(String[] args) throws Exception {
new MtApp().run();
}
private void run() throws Exception {
Runnable r = () -> {
while (get() == 0) ;
};
Thread thread = new Thread(r);
thread.start();
Thread.sleep(10);
set(5);
thread.join();
}
}