volatile定义
volatile是java内置的关键字,是java虚拟机提供的轻量级同步机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
保证可见性
在JMM(java内存模型)中,线程是采取拷贝的形式获取主内存的数据,在某个线程更改主内存数据后,某些线程内存里可能还是以前的老数据
无volatile时
public class VolatileTest {
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 = 1;
System.out.println(Thread.currentThread().getName()+"=>num="+num);
},"B").start();
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"=>num="+num);
}
}
A线程是无法感知到num的更新,所以一直无法结束
加上volatile
public class VolatileTest {
private static volatile 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 = 1;
System.out.println(Thread.currentThread().getName()+"=>num="+num);
},"B").start();
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"=>num="+num);
}
}
A线程能及时感知到num发生变化
不保证原子性
原子性:不可分隔的操作元,要不啥都不做,要不全部做
volatile无法保证原子性,想要保证原子性,可采取
- 对操作数据的代码段加锁
- 使用java.util.concurrent.atomic 下的数据
public class AtomicTest {
private static volatile int num = 0;
private static void add(){
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();
}
while (Thread.activeCount() >2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"=>num="+num);
}
}
用javap -c AtomicTest.class查看字节码汇编,可知
add()中num++被拆分为三步
读取,更改,写回!
多线程情况下,可能存在多个线程读取到同一数据,造成脏读!
禁止指令重排
源代码=>编译器优化重排=>指令并行也可能重排=>内存系统重排=>执行
底层会在不影响数据的情况下可能进行指令重排优化,但是在并发情况下可能回导致依赖错误,所以要禁止重排。