如何避免数据竞争 - 两个例子

时间:2013-12-19 09:03:56

标签: java parallel-processing atomic race-condition

我被告知以下代码示例具有数据争用条件(当然假设有多个线程):

class C { 
 private int x = 0; 
 private int y = 0; 

 void f() { 
     x = 1; 
     y = 1; 
 } 

 void g() { 
     int a = y; 
     int b = x; 
     assert(b >= a); 
 } 
} 

然而,我被告知以下"修复"没有数据竞赛:

class C { 
 private int x = 0; 
 private int y = 0; 

 void f() { 
     synchronized(this) { x = 1; } 
     synchronized(this) { y = 1; } 
 } 

 void g() { 
     int a, b; 
     synchronized(this) { a = y; } 
     synchronized(this) { b = x; } 
     assert(b >= a); 
 } 
}

可以理解,上面的例子还有其他问题,但我只是想知道为什么第二个代码块没有竞争条件。同步每个赋值语句如何消除数据竞争条件?一次只同步一个赋值语句有什么意义?

为了澄清,数据竞争的定义如下:

Data races: Simultaneous read/write or write/write of the same memory location

3 个答案:

答案 0 :(得分:1)

在第一个示例中,断言失败会注意到数据竞争条件。

那怎么可能呢? y> x应始终为false,因为yx之后写入并在x之前读取。

即使你考虑了

的所有交错
            Thread 1                 Thread 2
            ----------------------------------
            read y
            read x
                                     write x 1
                                     write y 1

您应始终拥有x <= y

但是在 安全 执行中,如果在执行read v期间write v,则无法保证该值读

v is 0
T1 write 1:   wwwwwwwww
T2 read   :         rrrrr 
T3 read   :               rrrrr

在这种情况下,T2读取的值可以是任何值,例如42。同时,T3读取的值保证为1。

在第一种情况下,ab可以是任何内容,因此断言可能会失败。 “修复”保证数据竞争(并发读/写)永远不会发生,并且ab将始终为0或1。

答案 1 :(得分:0)

谁告诉你这是错的;如果您单独同步,竞争条件(在x之前更改yassert;实际上,只有assert (x >= y);具有相同的问题)仍然存在。

JIT JVM可能会很好地执行锁定粗化并将两对赋值移动到单个synchronized块中,但语言语义无法保证这一点。

答案 2 :(得分:0)

synchronized关键字是关于读取和写入相同变量,对象和资源的不同线程。这在Java中不是一个简单的主题,但这里引用了Sun:

同步方法启用了一种简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都是通过同步方法完成的。

非常非常简单地说:如果有两个线程正在读取和写入相同的“资源”,比如名为foo的变量,则需要确保这些线程以原子方式访问变量。如果没有synchronized关键字,您的线程1可能看不到更改线程2对foo的影响,或者更糟糕的是,它可能只有一半更改。这不是你逻辑上所期望的。