1. volatile关键字修饰变量特点
不支持原子性。
- 可见性:对一个被volatile关键字修饰的变量,1. 写操作,这个变量的最新值会立即刷新回到主内存中。 2. 读操作,总是能够读取到这个变量的最新值,也就是这个变量最后被修改的值。 3. 当某个线程收到通知,去读取volatile修饰的变量的值的时候,线程私有工作内存的数据失效,需要重新回到主内存中读取最新的数据。
- 有序性:有时需要禁止重排序。
2. volatile内存语义
- 当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中。
- 当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,重新回到内存中读取最新共享变量。
- 所以volatile的写内存语义是直接刷新到主内存中,读的内存语义是直接从内存中读取。
3. volatile内存屏障
写屏蔽:写屏蔽之前的所有写操作都要写回到主内存。
读屏蔽:读屏蔽之后的所有读操作都在读屏蔽之后执行,都可以读到最新数据。
4. 指令重排
5. volatile的可见性
public class demo04 {
/*
volatile 可以让数据将线程中的数据副本更新到主内存中
去掉 volatile, 子线程中的 flag 会一直自己的数据副本, 不会进行更新
*/
static volatile boolean flag = true;
public static void main(String[] args) {
new Thread(()->{
System.out.println("thread start");
while (flag) {
}
System.out.println("exit");
}).start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
flag = false;
System.out.println("flag update: "+flag);
}
}
volatile加锁与解锁的过程。
6. volatile无原子性
volatile不能保证同一时刻,对于同一份数据,只有一个线程操作。volatile适合与布尔类型和int类型。
一下i++的例子证明volatile无原子性。
class Number {
// 使用volatile结果输出并不稳定唯一
volatile int num;
public void add() {
num ++;
}
public int get(){
return num;
}
}
public class demo05 {
public static void main(String[] args) {
Number number = new Number();
for(int i=0; i<10; i++) {
new Thread(()->{
for (int j = 0; j < 100; j++) {
number.add();
}
}).start();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(number.get());
}
}
7. volatile禁止重排
双重检查锁定(Double-Checked Locking, DCL)的单例模式:
public class SafeDoubleCheckSingleton {
private static volatile SafeDoubleCheckSingleton singleton;
private SafeDoubleCheckSingleton() {
}
public static SafeDoubleCheckSingleton getInstance() {
if (singleton == null) {
synchronized (SafeDoubleCheckSingleton.class) {
if (singleton==null) {
/**
* singleton 实例化:分配内存空间、初始化对象、将对象指向分配的内存空间
* JMM 可能会进行指令重排: 分配内存空间、将对象指向分配的内存空间、初始化对象
* 多线程场景下, 会出现问题
* 为 singleton 添加 volatile 关键字, 可解决这个问题
*/
singleton = new SafeDoubleCheckSingleton();
}
}
}
return singleton;
}
}