gpt4 book ai didi

java - 没有同步顺序的程序

转载 作者:行者123 更新时间:2023-12-05 08:21:55 25 4
gpt4 key购买 nike

我正在查看我发现的最简单的示例之一,并开始推理 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)。我想我也明白为什么:因为有 no synchronization order在这两个 Action 之间,因此没有happens-before,根据JLS,这是一个数据竞赛:

When a program contains two conflicting accesses that are not ordered by a happens-before relationship, it is said to contain a data race.

因此读取 ab 是两个有趣的读取,所以看到 b=1a=0是允许和可能的。

现在这又允许 JVM 在 writer 中进行锁粗化,所以它变成:

 void threadX() {
synchronized(this) {
a = 1;
b = 1;
}
}

我的问题是,如果读者最初是这样写的:

void threadY() {
synchronized(this) {
int r1 = b;
}

synchronized(this) {
int r2 = a;
}
}

还会允许锁粗化吗?我认为我知道答案,但我也想听到有根据的解释。

最佳答案

是的,这是允许的。


这里有一个简单的解释。

记住 synchronized block :

  1. 原子执行:
    • 当另一个线程持有相同的锁时,一个线程不能进入一个synchronized block
    • 当线程进入synchronized block 时,它会立即看到在先前执行的synchronized block 中所做的一切
  2. 以全局顺序执行,与每个线程的程序顺序一致(你说的同步顺序)

换句话说,synchronized block 总是以全局顺序以原子方式执行。不同的执行在 synchronized block 如何交错方面可能不同,但情况总是如此:

  1. threadX() 中的第一个 synchronized block 总是在第二个 block 之前执行
  2. 对于来自 threadY()synchronized block 也是如此

有 6 种可能的交错:

threadX          threadY           threadX          threadY             threadX          threadY       
------------------------------- ------------------------------- -------------------------------
synchronized { | synchronized { | synchronized { |
a = 1; | a = 1; | a = 1; |
} | } | } |
synchronized { | | synchronized { | synchronized {
b = 1; | | int r1 = b; | int r1 = b;
} | | } | }
| synchronized { synchronized { | | synchronized {
| int r1 = b; b = 1; | | int r2 = a;
| } } | | }
| synchronized { | synchronized { synchronized { |
| int r2 = a; | int r2 = a; b = 1 |
| } | } } | }
(Case A) (Case B) (Case C)


threadX threadY threadX threadY threadX threadY
------------------------------- ------------------------------- -------------------------------
| synchronized { | synchronized { | synchronized {
| int r1 = b; | int r1 = b; | int r1 = b;
| } | } | }
| synchronized { synchronized { | synchronized { |
| int r2 = a; a = 1; | a = 1; |
| } } | } |
synchronized { | | synchronized { synchronized { |
a = 1; | | int r2 = a; b = 1; |
} | | } } |
synchronized { | synchronized { | | synchronized {
b = 1; | b = 1; | | int r2 = a;
} | } | | }
(Case D) (Case E) (Case F)

当您在 threadY() 中合并 synchronized block 时:

void threadY() {                    void threadY() {          
synchronized(this) { synchronized(this) {
int r1 = b; int r1 = b;
} => int r2 = a;
synchronized(this) { }
int r2 = a; }
}
}

然后,您实际上只保留来自 threadY()synchronized block 彼此相邻的情况:即情况 A、C 和 D。

由于这次优化后没有出现新的可能执行,那么这次优化是合法的。


对于更严格和详细的解释,我建议:

  1. J. Manson's Ph.D. Thesis on JMM 中的“锁定粗化”一章
  2. 锁粗化 example在 A. Shipilev 的一篇文章中,在 the answer above 中推荐

关于java - 没有同步顺序的程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69293751/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com