Java的多线程2:jmm内存模型
概述
jmm内存模型是一种抽象的概念,不是真实存在的,也可以说它是一种规范。它规范定义程序各个变量的访问方式,线程运行时,它规定所有变量都存储在主内存中,为一块共享内存区域,所有的线程都可以访问,但线程对变量的操作必须在自己的工作内存(线程被创建时,jvm就会为它创建一个工作内存空间)中进行,不能直接对主内存的变量做操作,只有在工作内存对变量完成操作后再写完主内存,因为各个线程是无法访问对方的工作内存,所以线程的通讯必须通过主内存完成。
注意:此处的变量是实例变量,静态变量等。不包括局部变量和方法参数,它们是线程私有,不会被共享也不存在竞争。
作用
大家都知道Java号称一次编译到处运行,但是否想过不同的平台不同的硬件内存为何执行的结果都一样?这就是jmm内存模型,它能屏蔽各种硬件和操作系统的内存访问差异,实现Java程序在各种平台下都能达到一致的内存访问效果。它的目的是解决由于多线程通过共享的主内存通信时引发的本地工作内存与主内存数据不一致,也就是所谓的线程安全问题,它通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供如何处理可见性,原子性,有序性建立的模型。
jmm内存模型内存交互操作
主内存与工作内存相互交互由8种操作来完成,它们每一种操作都是原子的,不可再分:
- lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态;
- read(读取):作用于主内存的变量,它把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用;
- load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量放入到工作内存的变量副本中;
- use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎;
- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量;
- store(存储):作用于工作内存的变量,把工作内存中的一个变量值传递到主内存中,已便随后的write的操作;
- write(写入):作用于工作内存的变量,它把strore操作从工作内存中的一个变量的值传递到主内存变量中;
- unlock(解锁):作用于主内存的变量,把一个处于锁定的变量释放出来,释放后的变量才可以被其他线程锁定。
注意:8大操作必须按顺序执行,而没有保证必须是连续执行。
执行上述8种操作还必须满足规则:
- 不允许read和load,store和write操作单独出现;
- 线程不能丢弃它最近assign操作,变量在工作内存中改变了滞后都必须把变化同步回主内存中;
- 不允许一个线程无原因的吧数据从工作内存同步回主内存中;
- 一个新的变量只能在煮内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或者assign)的变量,即就是一个变量实施use和store操作之前,必须先自行assign和load操作;
- 一个变量在同一时刻只允许一条线程对其进行lock操作,lock操作可以被同一个线程重复执行多次,但多次lock后也要执行相同次数的unlock操作,才能最终解锁;
- 如果一个变量没有事先被lock锁定,那就不能对他unlock,也不能去unlock一个被其他线程锁定的变量;
- 对同一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。
jmm和jvm关系
这两者基本是没有关系的,jmm主内存,工作内存和jvm的堆,栈,方法区并不是同一个层次的内存划分。如果勉强对应起来,那从变量,主内存,工作内存的定义来看,主内存主要对应于堆中的对象实例部分,而工作内存则对应于虚拟机栈中的部分区域,更低层次说,主内存直接对应物理硬件的内存,而为了获取更好的运行速度,虚拟机可能会让工作内存优先存储于寄存器和高速缓存中,因为程序运行时主要访问的是工作内存。