Java同步方法和块

时间:2012-10-22 23:08:17

标签: java concurrency synchronization synchronized concurrent-programming

我正在尝试更全面地了解Java中多线程的同步。我理解使用synchronized关键字背后的高级思想,以及它如何在线程之间提供互斥。

唯一的问题是,即使你删除了使用这个主题比我认为需要的话题更混乱的synchronized关键字,我在网上和教科书中阅读的大多数例子仍能正常工作。

任何人都可以向我提供一个具体的例子,说明何时不包含synchronized关键字会产生错误的结果?任何信息将不胜感激。

2 个答案:

答案 0 :(得分:1)

关于竞争条件的事情是,如果你没有进行适当的同步,他们不会必然发生 - 事实上,它经常会很好地工作 - 但是一年之后,在半夜,你的代码将崩溃,一个你无法重现的完全不可预测的bug,因为这个bug只是随机出现。

竞争条件是如此阴险,因为它们总是使程序崩溃,并且它们或多或少地随机触发。

答案 1 :(得分:1)

您通常可以通过增加迭代次数来触发竞争条件。这是一个简单的例子,可以处理100次和1000次迭代,但在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提供了增量,获取和设置等原子操作。