java - 没有同步顺序的程序

标签 java multithreading jvm locking jit

我正在查看我发现的最简单的示例之一,并开始推理 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/

相关文章:

java - 没有主要 list 属性(但我在 jar 中有 list 文件)

java - 使用 XStream 将对象转换为 JSON

java - 使用 AsyncRestTemplate 多次制作 API 并等待所有完成

java - 从一个命令启动多个java程序(多个JVM)

java - MapReduce:增加并发映射器任务的数量

java - 用逗号分隔 HashMap 输出到 .csv

Java:Lucene 索引大小为 0kb 的空 _0.fdt 和 _0.fdx 文件

c# - Webapi2-完成一项任务后从 Controller 操作返回,但继续进行进一步的异步处理

Python: 'thread._local object has no attribute ' 待办事项'

java - 是否有一个jvm参数导致jvm在启动时创建一个hprof文件