目录
简单的说:volatile可实现并发变量的可见性,不能保证原子性操作,具有一定的有序性。
一、多线程下变量的不可见性
要解读该特性,我们首先需要有一定的JMM知识
1、什么是JMM
内存模型可以理解为在特定的操作协议下,对特定的内存或者高速缓存进行读写访问的过程抽象描述,不同架构下的物理机拥有不一样的内存模型,Java虚拟机是一个实现了跨平台的虚拟系统,因此它也有自己的内存模型,即Java内存模型(Java Memory Model, JMM)。
2、JMM中线程变量关系
JMM中每个线程都会向主内存中先备份一个共享变量副本,以便自身可以快速调用。
线程是不可以直接访问主内存的变量的,必须先备份共享变量副本。
由上述特性就出现了一下问题:
多个线程在进行同时读写时由于线程是优先调用自己备份的工作内存的,所以就会出现修改其他线程修改共享数据以后,读取的线程仍然保持着修改前的缓存,导致读取的数据仍然是之前的。
如图所示,我们可以看到子线程修改共享变量flag后同步到主内存,由于主线程内有flag的缓存,并且出于性能的分析,所以主线程仍然调用之前的flag变量,缓存未更新,这就会找出读取数据与更新数据不一致问题,也就是我们所说的线程不可见性。
3、解决方法1
使用syncronized加锁控制解决数据不同步问题
其底层工作原理如下:
(1)首先,线程获得锁
(2)线程清空工作内存
(3)从主内存拷贝共享变量到工作内存,保持为副本
(4)执行代码
(5)将修改后的副本的值刷新回主内存中
(6)线程释放锁
4、解决方法2
使用volatile使数据可见,
实现底层原理如下:
如图所示,实现步骤如下:
1、2、主内存备份数据到两个线程
3、子线程修改共享变量
4、子线程将修改的共享变量同步到主内存
5、主线程工作内存副本失效
6、主线程因为工作内存失效,向主内存同步数据,获取到最新数据
这便是volatile的数据可见性。
二、volatile多线程下的不保证原子性
volatile只能保证每个线程获取的数据是最新的,但不能保证线程安全,所以如果需要保证线程安全、进行原子性操作需要使用锁来解决。
1、解决线程安全方法一
使用锁来保证线程安全
2、解决线程安全方法二
使用原子类来保证线程安全,原子类里已经使用了volatile关键字,并且内部已对相关操作进行了封装,调用原子类的方法也可以保证线程安全。