资料内容:
1、引入
对于Java虚拟机的内存模型相信大家都不陌生了,对于每一个线程来说,栈是私有的,而堆是共
享的,也就是说在栈中的变量(局部变量、方法定义参数、异常处理器参数)不会在线程之间共享,
也就不会有内存可见性的问题,也不受内存模型的影响。
既然堆中的数据是线程共享的,那么一定会存在一个并发问题,但由于每个线程都有自己的本地
缓存,导致线程之间读取的数据可能不是最新的,从而出现数据不一致的问题。JMM定义了一种内存
模型,用于描述Java程序中多线程之间如何访问共享内存中的变量,从而解决了这个问题。
2、概述
JMM(Java Memory Model)是Java内存模型的缩写,是一种抽象的概念,定义了Java虚拟机
如何在计算机内存中存储和访问Java对象的方法。JMM规范主要用于解决多线程访问共享内存时的可
见性、有序性和原子性问题。下面是JMM的抽象示意图
所有的共享变量都存在主内存中;
每个线程都保存了一份该线程使用到的共享变量的副本。
如果线程A与线程B之间要通信的话,必须经历下面2个步骤:
1. 线程A将本地内存A中更新过的共享变量刷新到主内存中去。
2. 线程B到主内存中去读取线程A之前已经更新过的共享变量。
因此,线程A无法直接访问线程B的工作内存,那是因为工作内存是线程独有的,线程间通信必须
借助主内存,这也是JMM中的规定。当主内存中的共享变量被某个线程更新时,JMM会通过控制主内存
与每个线程的本地内存之间的交互,来提供内存可见性保证。因此通过JMM规范,有效的解决了以下
问题:
可见性问题:JMM保证对于一个线程对变量的修改,其他线程能够立即看到这个修改,从而
避免了线程之间读取数据不一致的问题;
有序性问题:JMM保证程序的执行顺序是有序的,即按照代码的编写顺序执行,从而避免了
出现代码执行顺序混乱的问题;
原子性问题:JMM保证对单个变量的读取和写入操作是原子性的,即不会出现数据竞争问
题。
3、JMM内存模型的实现
3.1、简介
在Java中存在着许多并发相关的关键字,如 valatile、synchronized、final 等,而这些
被大众所熟知的关键字便是Java内存模型封装了底层的实现后提供给开发人员进行使用。因此Java
内存模型除了定义了一套规范,还提供了一系列的封装了底层实现的关键字供开发者使用。
3.2、原子性
原子性指的是指一个操作是不可中断的,即多线程环境下,操作不能被其他线程干扰。在Java
中,最常用的便是使用关键字 synchronized 进行原子性的保证。
3.3、可见性
指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道该变更。对于Java中的普
通共享变量是不保证可见性的,因为数据修改被写入内存的时机是不确定的,多线程并发下很可能会
出现脏读的并发问题,因此便有着上面提及到的线程私有的工作内存。
每个线程的工作内存都保存了一份该线程使用到共享变量的副本,即主内存中的拷贝副本,在操
作数据时只能在本地的副本中进行操作,不能直接对主内存中的数据进行操作。同时不同线程间也是
不能直接进行通讯的,必须借助主内存来实现。
而在Java中提供了 volatile、synchronized、final 关键字来保证多线程操作时变量的可
见性,其中 volatile 关键字提供了这么一个功能,那就是被其修饰的变量在被修改后可以立即同
步到主内存中,被其他修饰的变量在每次使用之前都从主内存中刷新获取,从而保证可见性。