为什么关于线程的代码显示 a = 1 和 b = 3?为什么添加 "volatile" 不起作用?

why the code about thread shows a = 1 and b = 3? and why add a "volatile" didn't work?

代码如下:

public class ThreadCacheSample {
    int a = 1;
    int b = 2;

    public void change() {
        a = 3;
        b = a;
    }

    public void print() {
        if (a == 1 && b == 3) {
            // why this is happening?
            System.out.println("Thread[" + Thread.currentThread().getName() + "]Confused1 : a = 1, b = 3");
        } else if (a == 3 && b == 2) {
            // why this is happening, too?
            System.out.println("Thread[" + Thread.currentThread().getName() + "]Confused2 : a = 3, b = 2");
        } else {
            System.out.println("Thread[" + Thread.currentThread().getName() + "] b=" + b + ";a=" + a);
        }
    }

    public static void main(String[] args) {
        // create many many threads
        while (true) {
            // create test every time, to make sure a is 1 and b is 2 again
            final ThreadCacheSample test = new ThreadCacheSample();

            // one thread for changing
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    test.change();
                }
            }).start();

            // one thread for printing
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    test.print();
                }
            }).start();

        }
    }
}    

结果应该是 a = 1,b = 2,或者 a = 3,b = 3。

我的问题是:

  1. 为什么我会得到一些显示 "a = 1, b = 3" 或 "a = 3, b = 2" 的结果?

  2. 如果我将 "volatile" 添加到 a 和 b,为什么它不起作用? 我认为 volatile 会让每个线程只访问主内存,而不是他们自己的工作内存。所以 volatile 应该是一个解决方案。但是现实告诉我"volatile"也不是办法

    public class ThreadCacheSample {
        volatile int a = 1;
        volatile int b = 2;
        ...
    }
    

p.s。这是不是关于如何修复代码的问题。就是为什么会出现奇怪的结果?

添加 volatile 会单独增加 ab 修改的可见性(并在其修改的可见性之间添加排序依赖性)。它不是,但是,将任何类型的"atomicity"添加到它们的组合修改中。 "volatile"与否,a的修改和b的修改(以及a的读取和b的读取)仍然是独立行动!

您的两个 "confused" 答案都是可能的:

案例 1:

  1. 初始化,a == 1,b == 2
  2. 打印线程测试"a == 1"
  3. 更改线程集"a = 3"
  4. 更改线程集 "a = b" (3)
  5. 打印线程测试"b == 3"
  6. 困惑1

案例二:

  1. 初始化,a == 1,b == 2
  2. 更改线程集"a = 3"
  3. 打印线程测试"a == 3"
  4. 打印线程测试"b == 2"
  5. 困惑2

请注意,没有 volatile,您可能会得到更古怪的答案,例如看到 "a == 1, b == 3"!

的打印线程