Volatile
510字约2分钟
2024-08-08
Volatile
是 Java
虚拟机提供轻量级的同步机制
保证可见性
不保证原子性
禁止指令重排
可见性
public class Demo {
// 不加 volatile 程序就会死循环
// 加 volatile 可以保证可见性
private volatile static int num = 0;
public static void main(String[] args) {
new Thread(() -> {
while (num == 0) {
// 不能写打印 num 的语句,sout 里面有锁
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
System.out.println(num);
}
}
不保证原子性
原子性:不可分割
public class Demo2 {
// volatile 不保证原子性
private volatile static int num = 0;
public static void add() {
// num++ 不是原子性操作
num++;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
// 默认有 main 和 gc 两个线程在执行,线程数量大于 2 就礼让
while (Thread.activeCount() > 2) {
Thread.yield();
}
// 没加 volatile 之前每次结果都不一样,main 17630、main 18122
// 加了 volatile 之前每次结果都不一样,main 19122、main 18941
System.out.println(Thread.currentThread().getName() + " " + num);
}
}
如果不加 lock 和 synchronized,怎么保证原子性
使用原子类解决原子性问题
private volatile static AtomicInteger num = new AtomicInteger();
public static void add() {
// AtomicInteger +1 的方法
num.getAndIncrement();
}
java.util.concurrent.aotmic
这些类的底层直接和操作系统挂钩,在内存中修改值。Unsafe
类是一个很特殊的存在
指令重排
你写的程序,计算机并不是按照你写的那样去执行
源代码 -> 编译器优化的重排 -> 指令并行也可能会重排 -> 内存系统也会重排 -> 执行
int x = 1; // 1
int y = 2; // 2
x = x + 5; // 3
y = x * x; // 4
我们所期望的执行顺序是:1234
但是可能执行的顺序是:2134,1324
处理器在进行指令重排的时候,会考虑数据之间的依赖性,所以不会出现 4132 这种顺序
volatile 可以避免指令重排
内存屏障、CPU
指令
1、保证特定的操作的执行顺序
2、保证某些变量的内存可见性