有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

在Windows中具有竞争条件的java程序,但不在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);
        }
    }   
}

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上运行了该代码。它给出了预期的输出: -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) 个答案

  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);
        }
    
  2. # 2 楼答案

    I'd go out on a limb and claim that Hotspot under Linux using the server compiler while it doesn't on Windows is the more likely explanation: The compiler can replace the whole loop with a single expression which is something that HotSpot is definitely capable of. Adding any native method will make that impossible thereby making it much more likely to observe the race condition

    我猜情况也可能如此

    你试过让你的积分变量不稳定吗?这将防止可能发生的一些编译器优化

    public static volatile int IntegerVariable = 0;