我试图更全面地了解 Java 中多线程的同步。我了解使用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 提供了增量、获取和设置等原子操作。
关于Java 同步方法和 block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13021256/