这是比赛条件吗?
class A {
int x;
update() {
x = 5;
}
retrieve() {
y = x;
}
}
如果 update() 和 retrieve() 由两个不同的线程调用而没有持有任何锁,假设在共享变量的两次访问中至少有一次写入,这可以归类为竞争条件。但这真的是运行时的问题吗?
这是比赛条件吗?
class A {
int x;
update() {
x = 5;
}
retrieve() {
y = x;
}
}
如果 update() 和 retrieve() 由两个不同的线程调用而没有持有任何锁,假设在共享变量的两次访问中至少有一次写入,这可以归类为竞争条件。但这真的是运行时的问题吗?
没有锁,可能会发生三件事:
y
得到x
(5) 的新值。y
获取旧值x
(很可能为 0)。int
不是原子的,则y
可以获得任何其他值。在 Java 中,对 an 的读取int
是原子的,因此第三个选项不会发生。不保证其他语言的原子性。
使用锁定,前两个选项也可以发生。
但是,取决于内存模型,还有一个额外的挑战。在 Java 中,如果写入没有同步,则可以任意延迟到下一个同步点(synchronized
块结束或对volatile
字段的访问)。synchronized
类似地,可以从前一个同步点(块的开始或对volatile
字段的访问)任意缓存读取。这很容易导致陈旧缓存引起的问题。最终结果是,即使第一个选项应该发生,第二个选项也可能发生。
在 Java 中,始终使用volatile
可从其他线程访问的字段,否则您将面临因内存访问重新排序而导致的难以调试的竞争条件。同样的警告也适用于使用类似于 Java 中的内存模型的其他语言 - 您可能需要告诉编译器不要进行这些优化。