synchronized关键字是否会阻止Java中的重新排序?

时间:2014-04-24 16:00:50

标签: java multithreading synchronized

假设我在Java中有以下代码

a = 5;
synchronized(lock){
    b = 5;
}
c = 5;

同步会阻止重新排序吗? a,b和c之间没有依赖关系。分配给第一个然后发生到b然后发生到c?如果我没有同步,那么语句可以以JVM选择的任何方式重新排序吗?

3 个答案:

答案 0 :(得分:6)

将作业锁定到b至少会在作业之前引入获取围栏,并在作业之后引入释放围栏。

这可以防止在获取栅栏移动到栅栏上方之后的指令,以及释放栅栏之前的指令移动到栅栏下方。

使用↓↑符号:

a = 5;
↓ 
b = 5;
↑
c = 5;

↓可防止指令移到它上面。 ↑可以防止指令移到它下面。

答案 1 :(得分:4)

  

同步会阻止重新排序吗?

它可以防止一些重新排序。您仍然可以在同步块之外和同步块内重新排序,但不能从同步块内部重新排序到外部。

  

a,b和c之间没有依赖关系。

这没什么区别。

  

对第一个的分配会发生在b然后发生在c吗?

是。但正如已经指出的那样,并非所有JVM都能保证这一点。 (见下文)

  

如果我没有同步,那么语句可以以JVM选择的任何方式重新排序吗?

是的,通过JVM和/或CPU指令优化器和/或CPU缓存,但不太可能给出没有明显理由怀疑改变a = 5的顺序;和b = 5;将提高绩效。

您可以看到更改缓存的可见性。即读取这些值的另一个线程可以看到b = 5;在a = 5之前;例如它们位于不同的缓存行上,如果它还没有同步。

答案 2 :(得分:4)

  

同步会阻止重新排序吗?

部分见下文。

  

对第一个的分配会发生在b然后发生在c吗?

否。正如dcastro指出的那样,可以将动作移动到同步块中。因此,允许编译器生成代码,该代码对应于以下语句:

synchronized (lock){
    a = 5;
    b = 5;
    c = 5;
}

还允许编译器对同步块内的语句重新排序,这些语句彼此之间没有依赖关系。因此,编译器还可以生成与以下语句对应的代码:

synchronized (lock){
    c = 5;
    b = 5;
    a = 5;
}
  

如果我没有同步,那么语句可以以JVM选择的任何方式重新排序吗?

嗯,我认为这是错误的问题,而且考虑 Java内存模型也是错误的方法。根据重新排序定义 Java内存模型 。实际上,它比大多数人想象的要容易得多。基本上,您只能在Java语言规范的§17.4.5中找到一条重要的规则:

  

当且仅当所有顺序一致的执行没有数据争用时,程序才能正确同步。如果程序正确同步,则程序的所有执行都将显示为顺序一致。

换句话说:如果完全忽略重新排序,并且对于程序的所有可能执行,两个线程都无法访问相同的内存位置,这两个位置都不是 volatile < / em>也不是 atomic ,并且至少有一个动作是写操作,然后会出现所有执行,就像没有重新排序一样。

简而言之:避免数据竞赛,您将永远不会观察到重新排序