Skip to content

JMM

Java Memory Model,它定义了主存、工作内存抽象概念,底层对应着CPU寄存器、缓存、硬件内存、CPU指令优化

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

可见性

存在的问题

java
static boolean run = true;
public static void main(String[] args) throws InterruptedException {
    new Thread(() -> {
        while (run) {
        }
    }).start();
    Thread.sleep(1000);
    run = false;
}

分析原因:

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

image.png

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

image.png

  1. 1秒后,main线程修改了run的值,并同步至主存,而t是从自己的工作内存中的高速缓存中读取这个变量的值,结果永远是旧值

image.png

上述问题就是可见性问题,可以通过加上volatile关键字解决

volatile

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

  • 如果是多个线程同时写这个变量,会失效,只能保证看到最新值,不能解决指令交错
java
volatile static boolean run = true;
public static void main(String[] args) throws InterruptedException {
    new Thread(() -> {
        while (run) {
        }
    }).start();
    Thread.sleep(1000);
    run = false;
}
  • synchronize语句块既可以保证代码的原子性,也同时保证代码块内的变量的可见性,但缺点是synchronize是属于重量级操作,性能相对较低
java
static boolean run = true;
final static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
    new Thread(() -> {
        while (run) {
            synchronized (lock){
                
            }
        }
    }).start();
    Thread.sleep(1000);
    run = false;
}

原子性

有序性