1.1多线程下变量的不可见性
1.1.1 说明
多线程并发执行下,多个线程修改共享变量,会出现一个线程修改了共享变量的值后,另一个线程无法直接看到修改的新值。
1.2 变量不可见性的内存语义
1.2.1 概述
JMM(Java Memory Model):java内存模型是java虚拟机规范中定义的内存模型,java内存模型是标准化,屏蔽了底层不同的计算机区别。
Java内存模型(Java Memory Model)描述Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节。
JMM有以下规定:
●所有的共享变量都存储于主内存。这里所说的变量指的是实例变量和类变量。不包含同部变量,因为局部变量是线程私有的,因此不存在竞争问题。
●每一个线程还存在自己的工作内存,线程的工作内存,保留了被线程使用的变量的工作副本。
●线程对变量的所有的操作(读,取)都必须在工作内存中完成,而不能直接读写主内存中的变量。
●不同线程之间也不能直接访问对方工作内存中的变量,线程间变量的值的传递需要通过主内存中转来完成。
1.3 变量不可见性的解决方案
1.3.1 概述
如何实现在多线程下访问共享变量的可见性:也就是实现一个线程修改变量后,对其他线程可见呢?接下来为大家介绍两种方案:第一种是加锁,第二种是使用volatile关键字
1.3.2 解决方案
加锁
某个线程进入sychronized代码块后,会执行下述过程
- 线程获得锁
- 清空工作内存
- 从主内存拷贝共享变量最新的值到工作内存
- 执行代码
- 将修改后的副本值刷回主内存
- 线程释放锁
Volatile
Volatile缓存可见性实现原理
底层实现主要是通过汇编lock前缀指令,它会锁定这块内存区域的缓存(缓存行锁定)并回写到主内存
IA-32架构软件开发者手册对lock指令的解释:
1)会将当前处理器缓存行的数据立即写回到系统内存。
2)这个写回内存的操作会引起在其他CPU里缓存了该内存地址的数据无效(MESI协议)
变量通过volatile修饰后,一旦该变量被再工作内存中被修改,就会被写入到主内存中,当经过总线,会因为总线一致性,使得其余工作内存中变量地址的数据失效
1.3.3 小结
volatile修改的变量可以在多线程并发修改下,实现线程间变量的可见性
2.1 volatile 特性概述
volatile 总体概述
在上节中,我们已经研究完了volatile可以实现并发下共享变量的可见性,除了olatle可以保证可见性外,
volatle还具备如下一些突出的特性:
volatile的原子性问题:volatile不能保证原子性操作。
禁止指令重排序:volatile可以防止指令重排序操作。
2.1.2 volatile不保证原子性
所谓的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行。volatile不保证原子性。
2.1.3 小结
在多线程环境下,volatile关键字可以保证共享数据的可见性,但是并不能保证对数据操作的原子性(在多线程环境下volatile修饰的变量也是线程不安全的)。在多线程环境下,要保证数据的安全性,我们还需要使用锁机制。
2.1.5 问题解决
使用锁机制
volatile 的LOCK
加在了原子操作再主内存的 store和assign