作用:volatile是Java提供的一种轻量级的同步机制
保证内存可见性
不保证原子性
防止指令重排序
public class VolatileDemo {
private static int num=0;
public static void main(String[] args) {
new Thread(()->{
while (num==0){
}
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num=1;
System.out.println("num="+num+"-----------跳出循环");
}
}
正常情况下应该是线程A一直无限循环,直到主线程执行完num=1后,线程A终止。实际主线程已经执行完num=1了,但是线程A并没有终止循环。
JMM
JMM:java内存模型
用于屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果
主要方法:
lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。
write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。
线程A与主线程各自都有自己的工作内存,使得主线程修改了num 的值,线程A并没有及时可见,因此产生上面的问题(内存不可见性)
使用场景:
1.将volatile修饰的状态变量改变时,会立刻通知其他线程
2.当读远多于写的情况下,可以将变量设置为volatile,则只需要在写操作上添加sync同步即可,每次写操作都会通知读操作去读取最新的值
指令重排
我们写的程序,计算机并不是按照我们所写代码顺序的那样去执行;volatile可以避免指令重排。
执行顺序:
源代码 -->编译器优化的重排–>指令并行也可能会重排–>内存系统也会重排–>执行
处理器在执行指令重排的时候,会考虑:数据之间的依赖性
内存屏障 CPU指令 作用:
1.保证特定的操作的执行顺序
2.可以保证某些变量的内存可见性
volatile是可以保持可见性,不保证原子性。由于内存屏障,可以保证避免指令重排序的现象产生
synchronized和volatile的区别
1.synchronized是修饰方法,代码块;volatile修饰的变量
2.synchronized,可以保证变量修改的可见性及原子性;volatile能实现变量修改的可见性,但无法保证原子性
3.synchronized可能会造成线程的阻塞;volatile不会造成线程的阻塞
4.synchronized标记的变量可以被编译器优化;volatile标记的变量不可以被编译器优化