Volatile介绍

2022-11-26,

介绍

volatile 是 Java 虚拟机提供的轻量级的同步机制,它可以保证可见性(缓存一致性协议)和有序性(禁止指令重排序,也就是通过内存屏障来实现),但是不保证原子性。


JMM

介绍

JMM 是一个抽象的概念,它描述的是一种规范。这些规范定义了程序中各种变量的访问规则。

JMM 定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写共享变量的副本。

规定

所有的共享变量都存储于主内存。这里所说的变量指的是实例变量和类变量,不包含局部变量,因为局部变量是线程私有的,因此不存在竞争问题。

每一个线程还存在自己的工作内存,线程的工作内存,保留了被线程使用的变量的工作副本。

线程对变量的所有的操作(读,写)都必须在工作内存中完成,而不能直接读写主内存中的变量。

不同线程之间也不能直接访问对方工作内存中的变量,线程间变量值的传递需要通过主内存中转来完成。

8种原子操作

lock(锁定):作用于主内存的变量,把一个变量标识为线程独占状态。

read(读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。

load(载入):作用于工作内存的变量,它把read操作从主存中得到变量放入工作内存的变量副本中。

use(使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。

assign(赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值赋值给工作内存的变量副本中,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。

store(存储):作用于工作内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用。

write(写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。

图示


Happens-Before 原则

介绍

happens-before 原则,是 JMM 中非常重要的一个原则,它是判断数据是否存在竞争、线程是否安全的主要依据,依靠这个原则,我们可以解决在并发环境下两个操作之间是否存在冲突的所有问题。JMM 规定,两个操作存在 happens-before 关系并不一定要 A 操作先于B 操作执行,只要 A 操作的结果对 B 操作可见即可。

内容

程序顺序原则:一个线程内保证语义的串行性

volatile规则:volatile变量的写操作,先发生于读操作,这保证了volatile变量的可见性

锁规则:解锁(unlock)必然发生在随后的加锁(lock)前

传递性:A先于B,B先于C,那么A必然先于C

线程的start()方法先于它的每一个动作

线程的所有操作先于线程的终结(Thread.join())

线程的中断(interrupt)先于被中断线程的代码

对象的构造函数执行、结束先于finalize()方法


volatile与内存屏障

内存屏障

内存屏障其实也是一种JVM指令,Java内存模型的重排规则会要求Java编译器在生成JVM指令时插入特定的内存屏障指令,通过这些内存屏障指令来禁止特定的指令重排序。

另外内存屏障还具有一定的语义:内存屏障之前的所有写操作都要回写到主内存,内存屏障之后的所有读操作都能获得内存屏障之前的所有写操作的最新结果(实现了可见性)。因此重排序时,不允许把内存屏障之后的指令重排序到内存屏障之前。

内存屏障的功能

1 确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;

2 强制将对缓存的修改操作立即写入主存,利用缓存一致性机制,并且缓存一致性机制会阻止同时修改由两个以上CPU缓存的内存区域数据;

3 如果是写操作,它会导致其他CPU中对应的缓存行无效。

[☛ 重点:这里就是配合总线嗅探的地方了,通过内存屏障保证有序性的同时也保证修改的变量要及时写回主内存]

内存屏障种类

Volatile读写内存屏障详情

StoreStore

Volatile 写

StoreLoad (保证写入对所有处理器可见)

Volatile 读

LoadLoad

LoadStore


为什么无法保证原子性?

在插入内存屏障之前的操作并不是原子性的。多个线程可以修改,所以无法保证原子性。


常见应用

状态标记

public class VolatileDemo {
private static volatile boolean isOver = false; public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (!isOver)
}
});
thread.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
isOver = true;
}
}

double check(单例模式)

public class SingletonExample5 {
// 私有构造函数
private SingletonExample5() {}
// 单例对象 volatile + 双重检测机制 -> 禁止指令重排
private volatile static SingletonExample5 instance = null;
// 静态的工厂方法
public static SingletonExample5 getInstance() {
if (instance == null) { // 双重检测机制
synchronized (SingletonExample5.class) { // 同步锁
if (instance == null) {
instance = new SingletonExample5();
}
}
}
return instance;
}
}

Volatile介绍的相关教程结束。

《Volatile介绍.doc》

下载本文的Word格式文档,以方便收藏与打印。