我有一个关于 Java 内存模型的问题。给定以下示例:
action 1
action 2
synchronized(monitorObject) { //acquire
action 3
} //release
action 4
acquire
和 release
可以是任何同步边缘(锁定、解锁、启动线程、加入线程、检测线程中断、volatile-write、volatile-read、等等)
是否保证action 3
在获取之前不能移动并且在释放之后不能移动? p>
是否保证 action 2
不能在获取之后(无论是在发布之前还是发布之后)和action 4
不能在发布之前移动(无论是在获取之前还是之后)?
对于编译器的重新排序操作,与边同步的“双向障碍”也是如此吗?
编辑 1 我很担心这一点,因为如果与边缘同步不是双向重新排序障碍,编译器可以通过将锁获取转移到其他人来简单地创建死锁。
或者甚至不需要双向重新排序障碍来防止这种情况发生,因为无法将锁获取插入其他人,因为这会改变同步顺序?
编辑 2 操作 1、2、3 和 4 是 JMM 定义的“线程间操作” .
编辑 3 下面是一个显示重新排序如何导致死锁的示例:
x 和 y 是共享变量,syncA 和 syncB 可以被任何其他线程获取。但是使用下面的代码,就不可能出现死锁。
/* 1 */ synchronized(syncA) {
/* 2 */ x = 1;
/* 3 */ }
/* 4 */ y = 0;
/* 5 */ synchronized(syncB) {
/* 6 */ y = 1;
/* 7 */ }
但是,如果对 syncA 的获取被重新排序到 syncB block 中,这可能会导致死锁:
y = 0;
synchronized(syncB) {
y = 1;
synchronized(syncA) {
x = 1;
}
}
我认为这不是合法的编译器转换,因为它会改变同步顺序。我对这个假设是否正确? Java 内存模型 (JMM) 的哪一部分允许/不允许这样做?
最佳答案
感谢assylias链接到 this question其中包含来自 JSR-133 Cookbook 的这张图片的答案:
根据这张图片,EDIT 3 的编译器转换是非法的,因为它重新排序了两个 MonitorEnter。
此外,此表还显示了哪些同步边是哪些类型的“重新排序障碍”以及其他操作。
谢谢你的帮助:)
关于java - 与边缘同步的编译器是否在两个方向上重新排序障碍?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26724443/