JVM的内存模型(JMM)
JVM中定义了自己的内存结构模型。JVM作为java程序一次编写到处运行的载体,为了兼容不同系统不同环境带来的内存的差异,规范制定了自己的内存操作实现,以实现不同平台下一致的并发效果。
image.png
在多线程的运行环境下,存在多个线程操作共享变量的情况,而JMM会为每个线程从主存中同步一份数据到线程栈,但是线程操作完自己的工作内存的变量,并不会马上同步到主存中,这就会带来多线程安全问题。
JMM的由来
硬件上内存与cpu的处理速度是有很大差异的,为了解决这种差异而引入了cpu高速缓存的设计。然而带来的问题是,多核cpu间缓存一致性问题。即在多线程环境下每个cpu缓存的值可能处在不一致的问题,导致线程操作出现问题。为了解决缓存一致性问题而引入了MESI协议。通过给缓存数据标记状态的形式,通过cpu嗅探机制实现数据的同步。由于硬件层面的升级带来的问题,无法改变硬件只能在软件上设计去解决,于是有了内存模型。
JMM的抽象类实于cpu高速缓存的设计。JMM中所有的共享变量都保存在主内存中,线程执行时从主内存拷贝副本到工作内存,对变量的修改从工作内存同步到主内存,实现线程间数据的通信。为了保证缓存的一致性,可以通过volatile、synchronized等关键字实现。
JMM的规定
- 线程从主内存中拷贝共享变量到工作内存
- 线程的所有操作都在工作内存中进行,而不是直接操作主内存
- 不同的线程间无法直接操作其他线程的工作内存数据
- 线程间数据的同步需要通过主内存进行
线程间的通信模型为
image.png
JMM中定义了8种操作
- read:把一个变量的值从主内存传输到工作内存中
- load:在 read 之后执行,把 read 得到的值放入工作内存的变量副本中
- use:把工作内存中一个变量的值传递给执行引擎
- assign:把一个从执行引擎接收到的值赋给工作内存的变量
- store:把工作内存的一个变量的值传送到主内存中
- write:在 store 之后执行,把 store 得到的值放入主内存的变量中
- lock:作用于主内存的变量,把一个变量标识为一条线程独占状态
- unlock:作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
JMM如何解决问题
- 原子性:保证一系列操作要么全部完成,要么全部失败
JMM解决原子性问题,可以通过synchronized关键字实现。底层通过monitorenter和monitorexit命令实现。
- 可见性:共享变量的修改对其他线程可见
volatile关键字可以保证对共享变量的修改,能够马上刷新到主内存,其他线程操作共享变量必须重新从主内存获取副本。当然synchronized,final等也能实现可见性。
- 有序性:保证程序执行的顺序按要求执行
实现禁止指令重排序可以通过volatile添加内存屏障、happen-before原则、synchronized加锁实现单线程处理。
网友评论