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;
}
分析原因:
- 初试状态,t线程刚开始从主存读取了run的值到工作内存
- 因为t线程要频繁的从主存中读取run的值,JIT编译器会将run的值缓存至自己工作内存中的高速缓冲区,减少对主存中run的访问,提高效率
- 1秒后,main线程修改了run的值,并同步至主存,而t是从自己的工作内存中的高速缓存中读取这个变量的值,结果永远是旧值
上述问题就是可见性问题,可以通过加上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;
}