在Windows中使用竞争条件进行编程,但在Ubuntu中进行编程

时间:2017-12-12 07:36:45

标签: java multithreading ubuntu race-condition

在旨在突出竞争条件的作业中,我们获得了以下代码

public class IncreaseDecrease {

    public static int IntegerVariable = 0;
    public static final int NUM_ITER = 5000000;

    public static void main(String[] args) throws Exception {

        Increase inc;
        Decrease dec;

        while (true) {          
            inc = new Increase();
            dec = new Decrease();

            inc.start();
            dec.start();

            inc.join();
            dec.join();

            System.out.println(IntegerVariable);
            IntegerVariable = 0;
            Thread.sleep(750);
        }
    }   
}

class Increase extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < IncreaseDecrease.NUM_ITER; i++) {
            IncreaseDecrease.IntegerVariable++;
        }
    }
}

class Decrease extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < IncreaseDecrease.NUM_ITER; i++) {
            IncreaseDecrease.IntegerVariable--;
        }
    }
}

如果每个线程在另一个线程读取之前可以更新该值,则该代码可以打印0,但是由于竞争条件不会发生这种情况,它可以打印-5000000和5000000之间的任何值。 我在windows和repl.it上运行了该代码,它给出了预期的输出: -310951 -1918567 -3374495 -3219135 -2286639 -3221055 -3794319 -2442047 -2776415 -3617391 但是在Ubuntu上,当我运行它时,它每次都给出0。

我的问题是,为什么会这样? Ubuntu是否以不同的方式管理线程,还是仅仅是我的计算机的特殊情况?

编辑: 在将增量放入不同的方法并向其添加一个操作之后,我观察了竞争条件。这是最终的代码:

public class IncreaseDecrease {

    public static int IntegerVariable = 0;
    public static final int NUM_ITER = 5000000;

    public static void main(String[] args) throws Exception {

        Increase inc;
        Decrease dec;

        while (true) {          
            inc = new Increase();
            dec = new Decrease();

            inc.start();
            dec.start();

            inc.join();
            dec.join();

            System.out.println(IntegerVariable);
            IntegerVariable = 0;
            Thread.sleep(750);
        }
    }
    public static void increment ()
    {
        IntegerVariable++;
        double a = Math.pow(3, 7);
    }
    public static void decrement()
    {
        IntegerVariable--;
        double a = Math.pow(3, 7);
    }
}

class Increase extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < IncreaseDecrease.NUM_ITER; i++) {
            IncreaseDecrease.increment();
        }
    }
}

class Decrease extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < IncreaseDecrease.NUM_ITER; i++) {
            IncreaseDecrease.decrement();
        }
    }
}

2 个答案:

答案 0 :(得分:1)

  

我想说明Linux下使用服务器编译器的Hotspot虽然不在Windows上,但更可能的解释是:编译器可以用单个表达式替换整个循环,这就是HotSpot绝对有能力。添加任何本机方法都会使其变得不可能,从而更有可能观察到竞争条件

我猜这也许就是这种情况。

您是否尝试过 IntegerVariable volatile ?这将阻止可能发生的某些编译器优化。

public static volatile int IntegerVariable = 0;

答案 1 :(得分:-1)

对Java中的线程存在一种常见的误解,即它们真正有效地交错处理处理。这实际上并不完全正确,不同系统上的不同JVM的工作方式不同。

这一切都源于JVM决定切换线程的时候。 JVM可能会在遇到Thread.sleep()或阻塞方法(如synchronizedlock时切换线程,但通常如果某个线程没有执行涉及阻塞的任何事情,它会让线程运行。

你的循环在递增和递减值时旋转,没有暂停。如果你在循环中添加Thread.sleep(0)调用,你可能会看到一个区别,因为你为JVM提供了更多机会来切换你的线程。

    for (int i = 0; i < IncreaseDecrease.NUM_ITER; i++) {
        IncreaseDecrease.IntegerVariable--;
        // Add this.
        Thread.sleep(0);
    }