JVM内存模型(JAVA Memory Model,简称 JMM)
JMM:用于定义变量(所有线程的共享变量,不能是局部变量)的访问规则。
JMM将内存划分为两个区:主内存区和工作内存区!
- 主内存区:真实存放变量
- 工作内存:主内存中变量的副本,供各个线程使用
注意:
- 各个线程 只能访问自己私有的工作内存(不能访问其他线程的内存,也不能访问主内存)
- 不同线程之间可以通过主内存间接的访问其他线程的工作内存。
各个线程只能访问自己的工作内存,那么A线程如何获取B线程的值?
- 将线程B的数据同步到主内存(Save:将工作内存的数据刷到主内存)
- 线程A获取主内存中的最新数据(Load:主内存到工作内存)
不同线程之间数据如何交互
刚才介绍了 Save 和 Load 两个动作,但是不同线程之间数据交互的步骤不仅仅只有这两个,下面我们完整介绍一下交互步骤。
- Lock : 将主内存中的数据,标识为一条线程的独占状态。
- Read : 将主内存中的变量,读取到工作内存中。
- Load:将上一步读取的变量,拷贝到变量副本当中。
- Use:将工作内存中的变量副本,传递给线程去使用。
- Assign:将线程正在使用的变量,传递给工作内存中的变量副本。
- Store:将工作内存中变量副本的值,传递到主内存中。
- Write:将变量副本作为主内存中的变量进行储存。
- UnLock:解除线程的独占状态。
JVM要求以上的八个动作必须是原子性的。
但是JVM对于64位的数据类型(long/double)有非原子性协议!!long和double占用的字节数都是8,也就是64bits。在32位操作系统上对64位的数据的读写要分两步完成,每一步取32位数据。这样对double和long的赋值操作就会有问题:如果有两个线程同时写一个变量内存,一个进程写低32位,而另一个写高32位,这样将导致获取的64位数据是失效的数据。因此需要使用volatile关键字来防止此类现象。volatile本身不保证获取和设置操作的原子性,仅仅保持修改的可见性。但是java的内存模型保证声明为volatile的long和double变量的get和set操作是原子的。