Volatile
java 中一个关键字,作用
- 保证可见性
- 不保证原子性
- 禁止指令重排
上一篇JMM 中了解到每个线程都是自己私有内存,而且线程对从主内存中加载的变量做的修改,对其他线程不可见的,volatile 保证线程之间可见性
测试可见性
private static int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (num == 0) {
}
},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
num++;
},"B").start();
TimeUnit.SECONDS.sleep(1);
System.out.println(num);
}
- num 没有添加 volatile 时,线程A 执行后,等待num 值变化,线程B 对num++后,num=1,由于线程线程不可见,线程A不知道num发生了变化,一直在死等。
- 添加
volatile
重新致性,正常结束退出
private volatile static int num = 0;
不保证原子性
public class AtomicMain {
private static volatile int n = 0;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
add();
}
}, "A").start();
}
TimeUnit.SECONDS.sleep(1);
System.out.println("当前值:" + n);
}
public static void add() {
n++;
}
}
- 正常运行,返回结果应该是 10000, 实际运行结果每次都不太一样,也验证了
volatile
不能保证原子性,怎么保证原子性
- synchronized
public synchronized static void add(){
n++;
}
- 原子变量
private static AtomicInteger atomicInteger = new AtomicInteger();
public static void add() {
atomicInteger.getAndIncrement();
}
以上两种方式可以解决原子性问题
禁止指令重排
编写的代码到执行,计算机会进行对此优化,
写的代码不一定按照自己写的顺序执行
- 代码优化过程
源代码->编译器(优化重排)->指令并行重排-> 内存系统的重排-> 最终执行
代码经过几次优化,重排,并不一定按照编码顺序执行,被volatile
修饰后,会禁止指令重拍,但是对于非volatile
修饰前后的代码仍会重排