假设我在Java中有以下代码
a = 5;
synchronized(lock){
b = 5;
}
c = 5;
同步会阻止重新排序吗? a,b和c之间没有依赖关系。分配给第一个然后发生到b然后发生到c?如果我没有同步,那么语句可以以JVM选择的任何方式重新排序吗?
答案 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 ,并且至少有一个动作是写操作,然后会出现所有执行,就像没有重新排序一样。
简而言之:避免数据竞赛,您将永远不会观察到重新排序。