目录
volatile概述
作用:
特点:
对比:
底层原理:
volatile如何保证可见性的
volatile如何保证有序性
为什么volatile不保证原子性
volatile概述
volatile可以理解为轻量级的synchronized。但他与synchronized不同,volatile只修饰变量,无法修饰方法和代码块。
作用:
防止指令重排序; 保持线程的可见性(可保障读操作是绝对准确的)
作用的解释:
1、计算机里面的指令重排序:计算机可能不按照我们写的代码指令顺序执行,而是会自己进行指令重排序(流水线技术)
2、可见性:指其中一个线程对共享变量做出操作的时候,其他线程可以准确无误地读出它的改变
(注意:final也有防止指令重排序,保障多线程下线程可见性的作用)
特点:
虽然volatile可保障读操作是绝对准确的(保证可见性),但读完之后往回写时,不能保证他是写准确的(不保证原子性)。(可能在“读后 写前”已经被其他线程修改啦)
对比:
synchronized锁可以保证写后读,保证读写操作的准确性(保证可见性+原子性),但是volatile锁只保证读操作的准确性
底层原理:
volatile如何保证可见性的
当对volatile变量进行写操作的时候,JVM会向处理器发送一条lock前缀的指令,当CPU发现这个指令时,立即会做两件事情:
将当前内核中的线程的缓存中的变量回刷新到系统主存中;
通知其他内核里缓存的该共享变量内存地址无效;
由此保证多线程可见性。
MESI的核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。
volatile如何保证有序性
volatile是通过内存屏障来禁止指令重排的,这就保证了代码的程序会严格按照代码的先后顺序执行。这就保证了有序性。被volatile修饰的变量的操作,会严格按照代码顺序执行,load->add->save 的执行顺序就是:load、add、save。如经典的双重校验锁必须加volatile的问题,就是因为volatile加了内存屏障。
为什么volatile不保证原子性
volatile仅保证单个操作的原子性:在Java中,volatile仅保证对单个volatile变量的读操作和写操作是原子的。这意味着,当一个线程读取或写入一个volatile变量时,这个操作是不可中断的。但是,这并不意味着复合操作(如i++)是原子的。
复合操作的非原子性:对于像i++这样的复合操作,它实际上包含三个步骤:读取i的值、将i的值加1、将新值写回i。由于volatile只能保证每个步骤的原子性,而不能保证整个复合操作的原子性,因此当多个线程同时执行这样的操作时,就可能出现数据不一致的问题。
内存可见性与原子性的区别:volatile通过内存屏障等机制保证了变量的内存可见性,即当一个线程修改了volatile变量的值后,这个新值对其他线程是立即可见的。然而,这并不意味着volatile能够保证复合操作的原子性。内存可见性和原子性是两个不同的概念,前者关注的是变量值的传播速度,而后者关注的是操作的不可分割性。