线程不安全递减/递增 - 为什么大多数是正面的?

Naz*_*zin 6 java multithreading increment decrement

我想知道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));
    }
}
Run Code Online (Sandbox Code Playgroud)

线程类:

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--;
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

我已经运行了几次程序,并且总是得到更积极的结果然后是否定的.我也试图改变哪些线程开始的顺序,但这没有改变.一些结果:

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
Run Code Online (Sandbox Code Playgroud)

小智 6

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

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

如果为true,那么这表明这有什么表明线程交互?

现在,为了好玩,使Magic.counter挥发性 - [结果如何]改变?

怎样去除volatile和围绕if/elselock?(A lock确保完整的记忆围栏并建立一个关键区域.它应该总能产生完美的结果.)

快乐的编码.


需要考虑的事项:

  1. 代码只能看到小于或大于零,而不是整体漂移/变化:只需+1或-1就可以提升尺度.(扩展收集的数据可能更有用.)
  2. 执行"else"分支需要花费更长的时间,因为需要跳转; 通常这是一个非问题,但超过1000万个周期......一两个并不多.
  3. 在变量的可见性方面,缺乏易失性/内存栅栏会留下很多背风Magic.counter.(我相信符合标准的JVM实际上可能会产生更糟糕的结果...)
  4. ++--运营商本身非原子.
  5. 线程交织通常是"非确定性的"; 如果跨多个核心执行则更少.