尝试理解以下主张:
当 2 个或更多线程尝试在不同步的情况下访问同一非最终变量时,就会发生数据竞争。不使用同步可能会导致进行其他线程不可见的更改,因此可能会读取过时的数据,这反过来可能会产生无限循环、损坏的数据结构或不准确的计算等后果。此代码可能会导致无限循环,因为读取器线程可能永远不会观察到写入器线程所做的更改:
class Waiter implements Runnable {
private boolean shouldFinish;
void finish() { shouldFinish = true; }
public void run() {
long iteration = 0;
while (!shouldFinish) {
iteration++;
}
System.out.println("Finished after: " + iteration);
}
}
class DataRace {
public static void main(String[] args) throws InterruptedException {
Waiter waiter = new Waiter();
Thread waiterThread = new Thread(waiter);
waiterThread.start();
waiter.finish();
waiterThread.join();
}
}
我的想法是,
- 在 Java Reader/Writer 中,像 boolean 这样的原始数据是原子操作。
- 即使读取器线程未能观察到写入器线程在一个循环中所做的更改,它也应该能够在下一个循环中看到更改。
没有这种情况吗?为什么?因为读取器线程可能永远不会屈服于写入器线程?(如果是这样,我认为它不应该被称为竞争条件,对吧?)
不。
每个线程都可以自由创建任何字段的本地缓存副本(通常,它们会这样做)。除非有理由更新这些缓存,否则 JVM 可能不会更新这些缓存,如果它愿意的话,可能会持续几天(规范没有对 JVM 提出任何这样做的要求)。
要强制更新,您可以使用
volatile
、synchronized
或 java 内存规范的 Java 内存模型部分中列出的任何其他 Happens Before 关系。您的代码的此类 HB 关系为零,因此 JVM 无需更新。换句话说,一个线程认为
shouldFinish
是true
,另一个线程认为是false
,并且他们继续认为是几个小时、几天或永远,或者不是 - JVM 规范根本没有指定任何一种方式。