谈谈对Volatile的理解
- volatile是JVM提供的轻量级的同步机制
volatile有三大特性
- 保证可见性
- 不保证原子性
- 禁止指令重排
JMM是什么
- JMM(Java内存模型,简称JMM)本身是一种抽象的概念并不真实存在
- 由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存,工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储到主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作必须在工作内存中进行。
- 首先要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成。
数据传输速率:硬盘 < 内存 < < cache < CPU
上面提到的概念 主内存 和 工作内存:
- 主内存:主要包括本地方法区和堆
- 工作内存:属于该线程私有的栈和对主存部分变量拷贝的寄存器(包括程序计数器PC和cup工作的高速缓存区)
JMM的特性
JMM的三大特性:可见性,原子性,有序性
volatile只保证了两个,即可见性和有序性,不满足原子性
可见性(及时通知)
- 指的是当主内存区域中的值被某个线程写入更改后,其它线程会马上知晓更改后的值,并重新得到更改后的值。
- 例如:有三个线程往主线程修改变量age=20,三个线程各自变量拷贝一份,其中一个线程在自己的工作空间将20改成了25,然后将工作内存中的值,写入主内存,主内存中的值被修改后,其他工作内存中的值会马上获得通知。
用volatile验证可见性
我们对于成员变量没有添加任何修饰时,是无法感知其它线程修改后的值
class MyData {
// 定义int变量
int number = 0;
// 添加方法把变量 修改为 60
public void addTo60() {
this.number = 60;
}
}
public class Test {
public static void main(String[] args) {
// 资源类
MyData myData = new MyData();
// 用lambda表达式创建线程
new Thread(() -> {
System.out.println("线程进来了");
// 线程睡眠三秒,假设在进行运算
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 修改number的值
myData.addTo60();
// 输出修改后的值
System.out.println("线程更新了number的值为" + myData.number);
}).start();
// main线程就一直在这里等待循环,直到number的值不等于零
while (myData.number == 0) {
}
System.out.println("main方法结束了");
}
}
最后线程没有停止,没有输出 main方法结束了 这句话,说明没有用volatile修饰的变量,是没有可见性的。
当我们给变量添加volatile关键字修饰:volatile int number = 0
,发现可以成功输出结束语句。
⭐ volatile 修饰的关键字,是为了增加 主线程和线程之间的可见性,只要有一个线程修改了内存中的值,其它线程也能马上感知,是具备JVM轻量级同步机制的。