6

我想知道java线程中不安全的递减/递增的结果,所以有我的程序:

主类:

public class Start {

    public static void main(String[] args) {

        int count = 10000000, pos = 0, neg = 0, zero = 0;

        for (int x=0; x<10000; x++) {

            Magic.counter = 0;

            Thread dec = new Thread(new Magic(false, count));
            Thread inc = new Thread(new Magic(true, count));

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

            try {
                inc.join();
                dec.join();
            } catch (InterruptedException e) {
                System.out.println("Error");
            }

            if (Magic.counter == 0)
                zero++;
            else if (Magic.counter > 0)
                pos++;
            else
                neg++;
        }

        System.out.println(Integer.toString(neg) + "\t\t\t" + Integer.toString(pos) + "\t\t\t" + Integer.toString(zero));
    }
}

线程类:

public class Magic implements Runnable {

    public static int counter = 0;

    private boolean inc;
    private int countTo;

    public Magic(boolean inc, int countTo) {
        this.inc = inc;
        this.countTo = countTo;
    }

    @Override
    public void run() {

        for (int i=0;i<this.countTo;i++) {

            if (this.inc)
                Magic.counter++;
            else
                Magic.counter--;
        }

    }
}

我已经运行了几次程序,并且总是得到比消极更多的积极结果。我也试图改变线程启动的顺序,但这没有改变。一些结果:

Number of results < 0 | Number of results > 0 | Number of results = 0

1103                8893                4
3159                6838                3
2639                7359                2
3240                6755                5
3264                6728                8
2883                7112                5
2973                7021                6
3123                6873                4
2882                7113                5
3098                6896                6
4

2 回答 2

6

我敢打赌,您会看到以下更改的完全相反的行为(即反转分支而不更改其他任何内容):

if (this.inc)
   Magic.counter--; // note change, and lie about `this.inc`
else
   Magic.counter++;

如果为 true,这可能表明有关线程交互的什么?

现在,为了好玩,让Magic.countervolatile -- [如何] 结果会发生变化?

用 a移除volatile和包围呢?(A确保了完整的内存栅栏并建立了一个关键区域。它应该始终产生完美的结果。)if/elselocklock

快乐编码。


需要考虑的事项:

  1. 该代码仅查看小于或大于零,而不是整体漂移/变化:+1 或 -1 是倾斜天平所需要的。(扩展收集的数据可能更有用。)
  2. 由于需要跳转,因此执行“else”分支所需的时间稍长;通常这不是问题,但超过 1000 万次循环......一两个并不多。
  3. 缺乏 volatile/memory-fence在变量的可见性方面留下了很大的Magic.counter余地。(我相信符合标准的 JVM 实际上会产生更糟糕的结果......)
  4. 和运算符本质++--是非原子的。
  5. 线程交错通常是“不确定的”;如果跨多个内核执行则更少。
于 2011-10-20T21:45:35.683 回答
0

一般来说,这是由于 Java 内存模型的工作方式造成的。您正在两个不同的线程中访问共享变量而没有同步。变量既不是声明为 volatile 的,也不是在运行原子操作。缺乏协调和 atomar 或 volatile 变量将导致 JVM 在执行时对线程代码进行内部优化。此外,未越过内存屏障(即 )的非易失性变量synchronized将导致每个线程的缓存值——因此在它们的缓存中存在两个冲突的线程本地副本。

鉴于 Java 中没有顺序一致性模型、复杂的运行时优化以及所使用的 JVM 和底层系统(单核或多核、超线程)的特殊性,不可能确定性地预测结果——仅仅因为它违反了几个多-Java 语言模型的线程约定。在同一台机器上运行完全相同的代码可能仍然会导致相似的结果,但是由于线程调度、其他操作系统进程的 CPU 使用率等的影响,它们可能不会完全相同。

以下是有关 JMM 的一些资源:http ://www.cs.umd.edu/~pugh/java/memoryModel/

于 2011-10-20T22:23:07.113 回答