1. volatile
- 保证可见性:程序会去主内存读取最新的改变
public class JMMDemo {
/*
* 不加volatile关键字,main线程将num改变,并写回主内存中,
* 但是A线程并没有去主内存读取最新的num值,所以线程没有中断
*
* volatile:提醒线程A去主内存读取最新值
* */
private volatile static int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while(num==0){
}
},"A").start();
TimeUnit.SECONDS.sleep(2);
num = 1;
}
}
- 不保证原子性
- 原子性:不可分割,例如:事务,同时成功,同时失败
/*
* num应该为2000,但是num++再底层计算是分好几步的
* volatile 不保证原子性
* 解决方案:
* 1.加锁
* 2.用并发包的原子类,解决原子性问题,效率最高
* (原子类直接和操作系统底层挂钩,在内存中修改值,unsafe)
* */
public class VolatileDemo {
//private volatile static int num = 0;
private volatile static AtomicInteger num = new AtomicInteger();
public static void main(String[] args) {
for (int i = 0; i < 10 ; i++) {
new Thread(()->{
for (int j = 0; j < 2000; j++) {
VolatileDemo.add();
}
}).start();
}
//保证只剩下:main和gc线程,证明num是最终值
while(Thread.activeCount()>2){
Thread.yield();
}
System.out.println("num= "+num);
}
private static void add(){
//num++;
num.getAndIncrement();//AtomicInteger+1 方法,CAS,效率极高
}
}
-
禁止指令重排
- 什么是指令重排:你写的程序并不是按照你写的那样去执行的
- 源代码>编译器的优化重排>指令并行也可能重排>内存系统也会重排–>执行
- volatile可以避免指令重排:内存屏障、CPU指令
- 作用
- 保证特定操作的执行顺序
- 可以保证某些变量的内存可见性(利用这些变量实现了内存)
- 根据自己的理解:Volatile修饰变量A,让A具有可见性,随时获取最新值,所以A不能改变自己的执行顺序,否则对其它影响比较大