在两个同步块和多个易失性读/写的情况下重新排序

时间:2013-07-08 19:42:12

标签: java multithreading concurrency synchronization volatile

考虑以下案例

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弱?

2 个答案:

答案 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编译器实现者,否则在实践中如何确保这是一个小细节。