定义
JMM 即 Java 内存模型 (Java memory model),在 JSR133 里指出了 JMM 是用来定义一个 统一的、跨平台 的内存模型,是缓存一致性协定,用来定义数据读写的规定。
内存可见性
在 Java 中,不同线程领有各自的公有 工作内存 ,当线程须要读取或批改某个变量时,不能间接去操作 主内存 中的变量,而是须要将这个变量读取到线程的 工作内存 的变量正本 中,当该线程批改其变量正本的值后,其它线程并不能立即读取到新值 ,须要将批改后的值 刷新到主内存中 ,其它线程能力 从主内存读取到批改后的值。
指令重排序!
在执行程序时为了进步性能,编译器和处理器经常会对指令做重排序,指令重排序使得代码在 多线程 执行时会呈现一些问题。
其中最驰名的案例便是在 初始化单例时 因为 可见性 和重排序 导致的谬误。
单例模式
案例 1
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {if (singleton == null) {singleton = new Singleton();
}
return singleton;
}
}
以上代码是经典的 懒汉式 单例实现,但在多线程的状况下,多个线程有可能会同时进入if (singleton == null)
,从而执行了屡次singleton = new Singleton()
,从而毁坏单例。
案例 2
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();
}
}
}
return singleton;
}
}
以上代码在检测到 singleton
为 null 后,会在同步块中再次判断,能够保障同一时间只有一个线程能够初始化单例。但依然存在问题,起因就是 Java 中 singleton = new Singleton()
语句并不是一个 原子指令,而是由三步组成:
- 为对象分配内存
- 初始化对象
- 将对象的内存地址赋给援用
然而当通过 指令重排序 后,会变成:
- 为对象分配内存
- 将对象的内存地址赋给援用(会使得 singleton != null)
- 初始化对象
所以就存在一种状况,当线程 A 曾经将内存地址赋给援用时,但 实例对象并没有齐全初始化 ,同时线程 B 判断singleton
曾经不为 null,就会导致 B 线程 拜访到未初始化的变量 从而产生谬误。
案例 3
public class Singleton {
private static volatile Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();
}
}
}
return singleton;
}
}
以上代码对 singleton
变量增加了 volatile
润饰,能够阻止 部分指令重排序。
那么为什么 volatile 能够保障变量的可见性和阻止指令重排序?
volatile
原理
- 规定线程每次批改变量正本后 立即同步到主内存 中,用于保障其它线程能够看到本人对变量的批改
- 规定线程每次应用变量前,先从主内存中 刷新最新的值 到工作内存,用于保障能看见其它线程对变量批改的最新值
- 为了实现可见性内存语义,编译器在生成字节码时,会在指令序列中插入 内存屏障 来避免指令重排序。
留神:
- volatile 只能保障根本类型变量的内存可见性,对于援用类型,无奈保障援用所指向的 理论对象外部数据 的内存可见性。对于援用变量类型详见:Java 的数据类型。
- volilate 只能保障共享对象的 可见性 ,不能保障 原子性 :假如两个线程同时在做 x ++,在线程 A 批改共享变量从 0 到 1 的同时,线程 B 曾经正在应用 值为 0 的变量,所以这时候 可见性曾经无奈发挥作用,线程 B 将其批改为 1,所以最初后果是 1 而不是 2。
感谢您浏览本文,关注我的公众号“语冰 Yubing”可接管最新推送,外面也有我分享的一些优质资源。