Volatile 介绍
在多线程并发编程中 synchronized 和 volatile 都扮演着重要的角色,volatile 是轻量级的 synchronized, 它在多处理器开发中保证了共享变量的“可见性”。 可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。如果volatile变量修饰符使用恰当的话,它比 synchronized 的使用和执行成本更低,因为它不会引起线程上下文的切换和调度。
Java内存模型
其中本地内存并不真实存在,是一个抽象概念,涵盖了缓存,写缓冲区,寄存器以及其他硬件和编译器优化.
Volatile的特性
可见性:对 一个 volatile 变量 的 读, 总是 能看 到( 任意 线程) 对 这个 volatile 变量 最后 的 写入。
原子性:对 任意 单个 volatile 变量 的 读/ 写 具有 原子 性, 但 类似于 volatile++ 这种 复合 操作 不具 有 原子 性。
1 | /** |
1 | class VolatileFeaturesExample { |
以上两种代码等价.锁的 happens- before 规则保证释放锁和获取锁的两个 线程之间的内存可见性,这意味着对一个volatile变量的读,总是能看到( 任意 线程)对这个volatile变量最后的写入。
Volatile 内存语义
写: 当 写 一个 volatile 变量 时, JMM 会把 该 线程 对应 的 本地 内存 中的 共享 变量 值 刷 新到 主 内存。
读: 当 读 一个 volatile 变量 时, JMM 会把 该 线程 对应 的 本地 内存 置 为 无效。 线程 接下来 将从 主 内存 中 读取 共享 变量。
Volatile 内存语义的实现
JMM为实现Volatile内存语义,会对编译器和处理器重排序进行限制,限制如下:
- Volatile写之前的操作必然发生在volatile写之前
- Volatile读之后的操作必然发生在volatile读之后
- Volatile读之前的Volatile写必然发生在Volatile读之前
内存屏障类型:
实现:编译器 在 生成 字节 码 时, 会在 指令 序列 中 插入 内存 屏障 来 禁止 特定 类型 的 处理 器重 排序。
- 在每个Volatile写之前插入StoreStore屏障
- 在每个Volatile写之后插入StoreLoad屏障
- 在每个Volatile读之前插入LoadLoad屏障
- 在每个Volatile读之后插入LoadStore屏障