这个Java并发的问题, 有人想过吗?(一个很有挑战的问题)

在研究Java并发, 内存模型时, 遇到了一些问题?

public class VolatileDemo {
    final static int MAX = 5;
    static int initVal = 0;
    static volatile int b=0;
    public static void main(String[] args) {
        new Thread(() -> {
            int localVal = initVal;
            while (localVal < MAX) {
                if (initVal != localVal) {
                    b++;
                    System.out.printf("The iniVal is updated to [%d]\n", initVal);// A步骤
                    localVal = initVal;
                }
            }
        }, "Reader").start();

        new Thread(() -> {
            int localVal = initVal;
            while (localVal < MAX) {
                System.out.printf("The initVal will be cahnged to [%d]\n", ++localVal);
                initVal = localVal;

                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Updater").start();
    }
}

以上的代码中, A 步骤永远不会发生(我测试了很多次, 我知道如果给变量加了volatile之后可以解决这个问题)。为什么?

  1. 为了防止localVal = initVal;指令重排到if之前, 我在他们之前加了一个volatile 变量
  2. 因此我怀疑是内存上的原因:但是我加了一个sleep, 这个时间完全足够工作内存到主内存中取数据, 但是还是没有什么作用
  3. 最后, 我怀疑是什么原因导致initVal变量一直使用的是工作变量(没有从主内存中刷新过来(虽然我认为不可能)), 没有从主内存中获取最新的值: 但是, 如果是这样, 那下面的例子为什么会不断访问获取主内存中的值呢?因此而导致结果还小
public class UnsafeThread implements Runnable {

    private static int count = 0;
    public void increase(){
        count++;
    }
    public void run() {
        for (int i = 0; i < 1000000000; i++) {
            increase();
        }
    }

    public static void main(String[] args) {
        UnsafeThread myThread = new UnsafeThread();
        Thread thread1 = new Thread(myThread);
        Thread thread2 = new Thread(myThread);
        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
            System.out.println(count);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

感谢你能阅读到这, 如果知道答案, 望解惑一二, 谢谢

看看这个,对你帮助应该很大
https://www.cnblogs.com/dolphin0520/p/3920373.html

你的A条件哪里第一次都不能进去,永远都不满足条件,怎么执行?

个人见解。应该知道变量没加volatile时,jvm会通知调用该变量的线程同步该变量的值到工作内存中,这个是实时性的。如果不加,线程就会直接使用存储在工作内存中的变量。
代码执行的速度是迅速的,而没有使用volatile时,当第二个线程改变了变量时 initVal = localVal,这个需要先同步到主内存中,主内存还来不及将变量同步到第一个线程中。第一个线程已经结束了。还有就是你在第二个线程中的等待时间,变量的值早就同步好了。所以才会不要低估代码的运行速度。
你的第二个demo,如果是一直访问主内存中值,那结果应该是2000000000,小的原因就是访问的是工作内存中的值。

多线程2大特性,1:原子性,2:可见性,

原子性:我们对数据的操作或者业务的操作要符合独立原则,也就是每一个业务就是一个最小单位,这样就不会发生数据超出或者重复的问题,

可见性:我们对变量的操作,或者说对象的操作,如果有多个线程公用这一个变量,则必须保证可见性,因为jvm在使用变量的时候,会从堆栈空间将变量移入交换区,如果不保证可见性,呢么就会出现a线程更改了变量的值,但是这时候jvm还没有来得及刷新交换区将其值写入堆栈空间,这时候b线程来了,拿的还是原来的值,就会发生问题,所以,要保证可见性,其就可以保证在交换区的值,对b线程可见,呢么,b线程就可以拿到交换区的值,这就是你为什么加volititle就可以的原因
图片说明