共享模型之内存

46 阅读2分钟

1.Java内存模型

JMM即Java Memory Model,它定义了主存、工作内存抽象概念,底层对应着CPU寄存器、缓存、硬件内存、CPU指令优化等。 JMM体现在一下几个方面:

  • 原子性-保证指令不会受到线程上下文切换的影响
  • 可见性-保证指令不会受到CPU缓存的影响
  • 有序性-保证指令不会受到CPU指令并行优化的影响

2.可见性

退不出的循环 先来看一个现象,main 线程对 run 变量的修改对于 t 线程不可见,导致了 t 线程无法停止:

static boolean run = true;

public static void main(String[] args){
   Thread t =  new Thread(()->{
        while(run){
            //...
        }
    });
    t.start();
    Thread.sleep(1000);
    run = false; // 线程t不会预期一样停下来
}

分析:

  1. 初始状态,t线程刚开始从主内存读取了run的值到工作内存

image.png 2. 因为t线程要频繁从主内存中读取run的值,JIT编译器会将run的值缓存之自己的工作内存的高速缓存中,减少主存中run的访问,提高效率

image.png 3. 1秒之后,main线程修改了run的值,并同步至主存,但是t线程是从自己的工作内存中读取run的值,所以结果永远都是旧值

image.png

解决办法:

volatile(易变关键字) 它可以用来修饰成员变量和静态成员变量,它可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作volatile变量是直接操作主存

可见性vs原子性

前面的例子中体现的实际是可见性,它保证多个线程之间,一个线程队volatile变量的修改是对另一个变量可见的,不能保证原子性,仅用一个写线程,多个读线程的情况

注意:synchronized语句块既可以保证代码块的原子性,也同时保证代码块内变量的可见性,但缺点是synchroized是属于重量级操作,性能相对较低