我正在查看我发现并开始推理SO
(同步顺序)或更准确地说,缺少它的最简单示例之一。考虑下面的例子:
int a, b; // two shared variables
Thread-X:
void threadX() {
synchronized(this) {
a = 1;
}
synchronized(this) {
b = 1;
}
}
还有一个读者线程Thread-Y
:
void threadY() {
int r1 = b;
int r2 = a;
}
为简单起见,让我们假设Thread-Y
完全按此顺序读取:它肯定会先读取b
,然后a
(与写入相反)。
允许阅读线程查看[1, 0]
(就像之前b=1
发生的那样)。我想我也明白为什么:因为两个动作之间没有同步顺序,因此没有发生之前,根据这是一个数据竞争: a=1
JLS
当一个程序包含两个冲突的访问,而这些访问不是由先发生关系排序的,它被称为包含数据竞争。
因此阅读a
andb
是两个活泼的阅读,所以看到b=1
anda=0
是允许和可能的。
现在这反过来又允许 JVM 在 writer 中进行锁粗化,所以它变成:
void threadX() {
synchronized(this) {
a = 1;
b = 1;
}
}
我的问题是,如果读者最初是这样写的:
void threadY() {
synchronized(this) {
int r1 = b;
}
synchronized(this) {
int r2 = a;
}
}
仍然允许锁粗化吗?我想我知道答案,但我也想听听有根据的解释。
锁粗化(和重新排序)是允许的,因为同步读取器要么在粗化锁之前或之后被排序。当粗化锁被持有时,他们将永远无法看到发生了什么,因此无法观察到锁定代码内部的任何重新排序。
更多信息请参见:https : //shipilev.net/blog/2016/close-encounters-of-jmm-kind/#myth-barriers-are-sane
顺便说一句,好问题。前段时间我也在为这个特定的例子而苦苦挣扎:)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句