juc并发编程02——JMM模型(上)

1.java内存模型

在计算机中,为了解决主内存的速度跟不上处理器速度的问题,我们给每个处理器添加一级或多级高速缓存(如下图)。但是,每个处理器上缓存的数据如何保证一致性呢?

为了实现缓存数据的一致性,我们计算机中使用了缓存一致性协议。java中也有类似的机制来实现多线程的数据模型。

juc并发编程02——JMM模型(上)java的内存模型规定如下:

所有的变量均存在主内存中(这里及后文的变量指所有可能出现竞争的变量:包括成员变量、静态变量,不包括局部变量)

每个线程都有自己的工作内存,线程对变量的操作必须在工作内存中完成。不同线程之间的工作内存相互隔离。

那么这个内存模型在jvm中究竟是如何实现的呢?

主内存:对应堆中的实例对象

工作内存:对应虚拟机栈,虚拟机可能会对这部分内存进行优化,将其放入寄存器或者高速缓存中。

看如下实例。

public class Demo4 {
private static int i = 0;
    public static void main(String[] args) throws InterruptedException {
        new Thread(()-> {
            for(int j = 0; j < 1000000; j++) {
                i++;
            }
            System.out.println(�34;thread1 run pass");
        }).start();
        new Thread(()-> {
            for(int j = 0; j < 1000000; j++) {
                i++;
            }
            System.out.println("thread2 run pass");
        }).start();
        Thread.sleep(1000);
        System.out.println(i);
    }
}

运行结果如下:

thread1 run pass
thread2 run pass
1729677

为什么呢?如果您有jvm的基础,就应该知道,原来自增操作并不是原子操作。可能出现如下图的情况。后面我们将讲解如何解决这个问题。

juc并发编程02——JMM模型(上)

2.重排序

在编译或者运行期间,有可能会对指令进行重排序。有以下可能:

  • java编译器根据对java语义的理解进行重排序。
  • 现代处理器可能会对机器指令自主进行重排序。

在单线程的环境下,指令重排序可以在不改变执行结果的前提下优化代码的执行效率。但是多线程下指令重排序可能会导致一些问题。

public class Demo5 {
    private static int a = 0;
    private static int b = 0;
    public static void main(String[] args) {
        new Thread(() -> {
            if(b == 1) {
                if(a == 0) {
                    System.out.println("A");
                }
                if(a == 1) {
                    System.out.println("B");
                }
            }
        }
        ).start();
        new Thread(
                () -> {
                    a = 1;
                    b = 1;
                }
        ).start();
    }

按朴素的理解,上面代码的输出只有可能是"B"或者没有输出。但是上面a,b的赋值可能被重排序

public class Demo5 {
    private static int a = 0;
    private static int b = 0;
    public static void main(String[] args) {
        new Thread(() -> {
            if(b == 1) {
                if(a == 0) {
                    System.out.println("A");
                }
                if(a == 1) {
                    System.out.println("B");
                }
            }
        }
        ).start();
        new Thread(
                () -> {
                    b = 1;
                    a = 1;
                }
        ).start();
    }

因此可能出现结果为"A"的情况。