在 JAVA 中的线程之间共享变量

Sharing variable between threads in JAVA

当我没有在这个变量,发生了一些我无法理解的事情,请某人帮忙解释一下,代码片段如下:

public class TwoThreads {

private static int n = 0;
private static int called = 0;

public static void main(String[] args) {

    for (int i = 0; i < 1000; i++) {
        n = 0;
        called = 0;

        TwoThreads two = new TwoThreads();
        Inc inc = two.new Inc();
        Dec dec = two.new Dec();
        Thread t = new Thread(inc);
        t.start();
        t = new Thread(dec);
        t.start();
        while (called != 2) {
            //System.out.println("----");
        }
        System.out.println(n);

    }
}

private synchronized void inc() {
    n++;
    called++;
}

private synchronized void dec() {
    n--;
    called++;
}

class Inc implements Runnable {
    @Override
    public void run() {
        inc();
    }
}

class Dec implements Runnable {
    @Override
    public void run() {
        dec();
    }
}

}

1) 我期望的是执行后 "n=0,called=2",但主线程可能会在 while 循环中被阻塞;

2) 但是当我取消注释这一行时,程序如预期的那样:

//System.out.println("----");

3) 我知道我应该在 "called" 上使用 "volatile",但我无法解释为什么会出现上述情况;

4) "called"在特定线程的工作内存中是"read and load",但是为什么在"long"while循环之后它没有"store and write"回到主线程,如果不是,为什么简单的 "print" 行可以产生如此大的差异

您已同步写入数据(在 inc 和 dec 中),但未同步读取数据(在 main 中)。两者都应该同步以获得可预测的效果。否则,main 可能永远不会 "sees" inc 和 dec.

所做的更改

你不知道called++到底会在哪里执行,你的主线程会不断的产生新的线程,这会造成互斥,我的意思是每次只有一个线程可以make called++,因为方法是同步的,而你不知道每个线程都会是它。可能会执行两次n++或n--,你不知道这个,可能会执行十次n++,而主线程达到你的条件。

并尝试阅读有关数据竞争的内容

 while (called != 2) {
            //System.out.println("----");
 }

//.. place for data race, n can be changed

 System.out.println(n);

您需要在此处同步对 called 的访问:

while (called != 2) {
    //System.out.println("----");
}

我建议添加 getCalled 方法

private synchronized int getCalled() {
    return called;
}

并将called != 2替换为getCalled() != 2

如果您对为什么会出现此问题感兴趣,可以阅读 java 内存模型上下文中的可见性。