一个简单的Java Race Condition

时间:2012-07-30 19:31:03

标签: java race-condition

我只是想为竞争条件写一个例子:

MyParallelClass.java

public class MyParallelClass implements java.lang.Runnable {
    public int counter = 0;

    @Override
    public void run() {
        if (test.globalVar > 0) {
            for (int i = 0; i < 1000000; i++) {
                counter++;
            }
            test.globalVar--;
        }
    }
}   

test.java

public class test {
    public static int globalVar;

    public static void main(String[] args) {
        globalVar = 1;

        MyParallelClass a = new MyParallelClass();
        MyParallelClass b = new MyParallelClass();

        new Thread(a).start(); // Thread A
        new Thread(b).start(); // Thread B

        System.out.println(globalVar);
    }
}   

我认为会发生什么:

我认为如果线程A在线程B启动之前完全执行,则可以输出0

变量test.globalVar也可以这样操作:

Thread A                     -  Thread B  
checks if (globalVar > 0)
      looping ...               checks if (globalVar > 0)
      looping ...               execute all four bytecode commands of "test.globalVar--;"
      execute test.globalVar--;

因此test.globalVar的值为-1

因此,任何一个if语句都会被执行或两者都执行。

实际发生了什么:

我得到01作为main方法的输出。为什么我会01而不是0-1

4 个答案:

答案 0 :(得分:3)

您正在递减globalVar两次。最后globalVar的可能值为:

  • -1 - 如果一切正常并且两个线程在打印前正确递减了值

  • 0

    • 如果只有一个线程设法减少变量而第二个线程在打印之前没有设法完成

    • 如果globalVar同时递减

  • 1

    • 如果System.out.println()在两个线程完成之前设法执行(非常可能)。 globalVar确实被修改了,但在它已经打印之后

    • 由于可见性问题main线程看到原始globalVar值,而不是由不同线程修改的值。您需要某种同步或volatile关键字才能立即(或永远)查看其他线程所做的更改。

答案 1 :(得分:1)

System.out.println(globalVar);

不等待线程完成。线程可能在此时完成,也可能不完整。因此,值可以是01-1,具体取决于线程是否已完成,一个已完成或两者均未完成。

要有更好的测试,
  - 在线程中使用Thread.sleep()以确保存在延迟
  - 在不同的线程中使用不同的延迟,以更好地可视化竞争条件   - 您可能还想在线程中打印变量的值。通过这种方式,您可以使用三个线程(A,B和主线程),并获得更好的可视化效果。

答案 2 :(得分:0)

好问题。我想你需要运行更多测试。 : - )

您可以尝试将Runnable类中的循环更改为以随机毫秒数(500-1000)进行休眠。循环只有10次。看看你是否达不到预期的竞争条件。

我认为大多数计算机都太快了。你的简单循环可能没有做足够的工作来导致线程切换。

我喜欢这些问题,因为我一直遇到这样的错误。

答案 3 :(得分:0)

如果在任一线程递减值之前打印了值,则可以得到1。