考虑以下案例
Case 1:
action#1
volatile read var 1
volatile write var 1
volatile read var 2
volatile write var 2
action#2
对于上述案例1的行动#2重新排序行动#1我们能说什么
Case 2:
action#1
synchronized(new Object()){}
synchronized(new Object()){}
action#2
对于上述案例2的行动#2重新排序行动#1我们能说些什么呢?
对于案例2,我已经有了问题的答案 Is this a better version of Double Check Locking without volatile and synchronization overhead。这个问题的答案说案例2可以在行动#2和行动#1之间进行重新排序,因为JMM是一个比Roach Motel Model弱的模型。我认为zhong.j.yu是对的。
但是现在我从以下问题的答案中得到了一些矛盾的东西 Valid reorderings - under new JMM。 这显示了一个严格的罗奇汽车旅馆模型。
For Orignal Code
instanceVar1 = value ;// normal read operation, no volatile
synchronized(this) {
instanceVar2 = value2; //normal read operation, no volatile
}
instanceVar3 = value3; //normal read operation, no volatile
The below Ordering is not possible
Case 4:
instanceVar3 = value3; //normal read operation, no volatile
synchronized(this) {
instanceVar2 = value2; //normal read operation, no volatile
}
instanceVar1 = value ;// normal read operation, no volatile
这也是杰里米·曼森博客文章的结论 http://jeremymanson.blogspot.co.uk/2007/05/roach-motels-and-java-memory-model.html
此外,我想指出编译器在优化涉及内存屏障的代码时受到限制。 请参阅:http://jeremymanson.blogspot.in/2009/06/volatile-arrays-in-java.html 其中** arr = arr冗余读写未优化,因为arr 是一个不稳定的参考**。
我想说的是问题的答案 在性质上几乎没有矛盾,两者似乎都是正确的。 问题1:Valid reorderings - under new JMM 问题2:Is this a better version of Double Check Locking without volatile and synchronization overhead
我们如何决定哪个Point JMM比Roach Motel Model弱?
答案 0 :(得分:3)
问题1:
另一个好的参考点是Reodering Grid(我经常在这里引用)。它说的有用的是NormalLoad
后跟MonitorExit
无法重新排序。在这种情况下,instanceVar1 = value ;
的正常负载无法重新排序到synchronized(this) {
的监视器出口
问题2:
在脸上看起来似乎有矛盾。但实际上它说的是,因为没有其他线程可以与对象同步(因为你正在做new Object
),所以有理由认为没有必要担心多线程,因此能够删除和重新排序synchronized
方法。
这是基于Lock Elision背后的想法。
易失性自引用读/写 - 据我所知,即使存储器本身也没有死代码删除,因此编译器仍然需要遵守易失性存储的排序规则。
答案 1 :(得分:2)
我认为你正在应对无关信息的过载。出于所有实际目的,JMM与Roach Motel车型一样强大。在Case 2中的一个小异常只是因为其他线程显然无法获取锁,因此整个synchronized
块只是一个balast。允许JVM伪装它从未见过它。
JVM必须保证的是,在程序顺序写入volatile之前的所有写入操作必须能够被另一个已读取volatile值的线程观察到(分别为锁定释放/获取actons的类似保证)。除非您是JIT编译器实现者,否则在实践中如何确保这是一个小细节。