我被告知以下代码示例具有数据争用条件(当然假设有多个线程):
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
答案 0 :(得分:1)
在第一个示例中,断言失败会注意到数据竞争条件。
那怎么可能呢? y> x应始终为false,因为y
在x
之后写入并在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。
在第一种情况下,a
和b
可以是任何内容,因此断言可能会失败。
“修复”保证数据竞争(并发读/写)永远不会发生,并且a
和b
将始终为0或1。
答案 1 :(得分:0)
谁告诉你这是错的;如果您单独同步,竞争条件(在x
之前更改y
和assert
;实际上,只有assert (x >= y);
具有相同的问题)仍然存在。
JIT JVM可能会很好地执行锁定粗化并将两对赋值移动到单个synchronized
块中,但语言语义无法保证这一点。
答案 2 :(得分:0)
synchronized关键字是关于读取和写入相同变量,对象和资源的不同线程。这在Java中不是一个简单的主题,但这里引用了Sun:
同步方法启用了一种简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都是通过同步方法完成的。
非常非常简单地说:如果有两个线程正在读取和写入相同的“资源”,比如名为foo的变量,则需要确保这些线程以原子方式访问变量。如果没有synchronized关键字,您的线程1可能看不到更改线程2对foo的影响,或者更糟糕的是,它可能只有一半更改。这不是你逻辑上所期望的。