我试图更全面地理解 Java 中多个线程的同步。我理解使用 synchronized 关键字背后的高级思想,以及它如何在线程之间提供互斥。
唯一的问题是,即使您删除了使该主题比我认为需要的更混乱的 synchronized 关键字,我在网上和教科书中阅读的大多数示例仍然可以正常工作。
谁能给我一个具体的例子,说明什么时候不包括 synchronized 关键字会产生错误的结果?任何信息将不胜感激。
我试图更全面地理解 Java 中多个线程的同步。我理解使用 synchronized 关键字背后的高级思想,以及它如何在线程之间提供互斥。
唯一的问题是,即使您删除了使该主题比我认为需要的更混乱的 synchronized 关键字,我在网上和教科书中阅读的大多数示例仍然可以正常工作。
谁能给我一个具体的例子,说明什么时候不包括 synchronized 关键字会产生错误的结果?任何信息将不胜感激。
关于竞争条件的事情是,如果你不进行适当的同步,它们不一定会发生——事实上,经常它会工作得很好——但是一年后,在半夜,你的代码将因无法重现的完全不可预测的错误而崩溃,因为该错误仅随机出现。
竞争条件之所以如此阴险,正是因为它们并不总是让你的程序崩溃,而且它们或多或少是随机触发的。
您通常可以通过增加迭代次数来触发竞争条件。这是一个简单的示例,它适用于 100 和 1,000 次迭代,但在 10,000 次迭代(有时)时失败(至少在我的四核机器上)。
public class Race
{
static final int ITERATIONS = 10000;
static int counter;
public static void main(String[] args) throws InterruptedException {
System.out.println("start");
Thread first = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < ITERATIONS; i++) {
counter++;
}
}
});
Thread second = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < ITERATIONS; i++) {
counter++;
}
}
});
first.start();
second.start();
first.join();
second.join();
System.out.println("Counter " + counter + " should be " + (2 * ITERATIONS));
}
}
>>> Counter 12325 should be 20000
此示例失败,因为访问counter
未正确同步。它可能以两种方式失败,可能都在同一次运行中:
这个简单程序的修复方法是使用AtomicInteger
. 由于增量的问题,使用volatile
还不够,但AtomicInteger
提供了增量、获取和设置等的原子操作。