JAVA 内存模型(JMM)
< 返回列表时间: 2020-08-05来源:OSCHINA
什么是 JAVA 内存模型
Java 内存模型(Java Memory Model JMM)就是一种 符合内存模型规范 、 屏蔽了各种硬件和操作系统的访问差异 、保证了 Java 程序在各种平台下 对内存的访问都能保证效果一致 的 机制及规范 . 内存模型(Memory Model)
在了解 JAVA 内存模型之前让我们先来了解一下我们为什么需要内存模型. 为什么要有内存模型
我们都知道,计算机的每条指令是在 CPU 中执行的,而数据是存放在主存(计算器物理内存).计算机初期 CPU 的计算速率和内存读取写入数据的速度差不多,相安无事.但是随着 CPU 技术的高速发展,执行速度越来越快,内存数据读取写入跟不上 CPU 的处理速度,导致 CPU 每次操作都要耗费很长的时间等待数据获取.为了处理内存读取数据速率导致的性能瓶颈,在 CPU 和内存之间引入了 高速缓存 .
高速缓存 : 一份数据拷贝,速度快,内存小.
程序执行过程也就变成了:高速缓存会将计算用的数据拷贝一份,CPU 直接从高速缓存获取数据.计算后向缓存插入数据,缓存内的数据再写入主存.
随着 CPU 速度的进一步提升,高速缓存也从原来的一层衍生为 多级缓存 .
多级缓存 :按照数据读取顺序和 CPU 结合的紧密程度,分为 一级缓存(L1),二级缓存(L2)..... .每一级缓存中所存储的全部数据都是下一级缓存的一部分.
加入多级缓存以后,自然程序执行的时候就变成了,CPU 从 L1 查询数据,无数据查询 L2,逐级往下查询的模式.
众所周知,CPU 是有单核和多核之分.在单核 CPU 的时候,只需要一套 L1,L2,L3 缓存.但是当多核时,每个核心都必须有自己的一套 L1(甚至 L2)缓存,而共享 L3(或 L2)缓存.

随着计算机能力的更进一步发展,从单线程,变成了多线程.这时候问题出现了.下面我们分析一下单线程,多线程单核,多核的情况. 单线程:CPU 核心的缓存只有一个线程访问.缓存独占,不会出现数据访问冲突,数据不一致等问题. 单核 CPU,多线程:进程中多线程同时访问缓存,获取的缓存地址相同,不会出现缓存丢失等问题,但是会出现,多线程同时操作同一缓存数据导致数据错误的情况.(CPU 处理非原子性,多线程互相争抢运行,会出现处理一半共享数据被其他线程更新的情况-->并发) 多核 CPU,多线程:L3(共享缓存)情况如同单核多线程.单由于每个核心都有自己的 L1/L2,线程运行多核切换,多核内的缓存数据不一致.
也就是说,在多线程的情况下.缓存会出现 缓存一致性问题

学过 JVM 的亲们应该都知道,JVM 的 JAVA 即时编译器(JIT)会做 指令重排 来提升性能.计算机硬件也有类似的功能--> 处理器优化 . 处理器优化会把指令重排,也就是执行的顺序不会按照我们代码编写顺序,而且乱序的.那么在并发编程的情况下,这就会导致数据处理和判断错误,得到不是我们希望的结果.
那么我们怎么处理这些问题呢
铛铛铛 --> 主角 内存模型出场 什么是内存模型
内存模型 : 为了保证共享内存的正确性(可见性、有序性、原子性),内存模型 定义了共享内存系统中多线程程序读写操作行为的规范 .
本文主要讲解 java 内存模型,如果对内存模型的相关内容有兴趣,下篇 -->内存模型详解马上推出哦 对就是骗你关注. JAVA 内存模型
真正的主角登场了 JAVA 内存模型 Java Memory Model (JMM) (本文为 JDK5 开始使用的内存模型)
JMM 规定了 所有的变量都存储在主内存中 (类似计算机内存吧), 每条线程都有自己的工作内存 (像不像高速缓存),线程中的工作内存中保存了该线程中用到的变量(主内存中的拷贝). 线程对变量的所有操作都在工作内存中进行 . 线程之间工作内存不可互通 .数据传递依靠主内存.多线程运行的时候就类似于一个单核多线程的 CPU 有没有. Java 内存模型的实现
Java 中提供了一系列和并发处理相关的关键字,比如 volatile、synchronized、final、concurren包 等.其实这些就是 Java 内存模型封装了底层的实现后提供给程序员使用的一些关键字.
关键字太多,本文只对并发编程中要解决 原子性 、 有序性 和 一致性 的关键字的进行介绍. 原子性
众所周知,Java 中可以使用 synchronized 来保证方法和代码块内的操作是 原子性 的. synchronized 的实现原理依赖于提供的两个高级的字节码指令 monitorenter 和 monitorexit . 可见性
Java 内存模型是通过在 变量修改后将新值同步回主内存 , 在变量读取前从主内存刷新变量值 ,实现缓存数据等于主内存数据的方式来实现.
Java 中 volatile 关键字就提供了这种功能,被 volatile 修饰的变量,修改后会立即同步到主内存,在使用前,从主内存获取最新数据使用,而不是工作内存的缓存数据.这就保证,多线程情况下数据的 可见性 .
除了 volatile ,Java 中的 synchronized 和 final 两个关键字也可以实现可见性.只不过实现方式不同. 有序性
Java 中,可以使用 synchronized 和 volatile d 都可以保证多线程之间操作的 有序性 . volatile 关键字会禁止 指令重排 . synchronized 关键字保证同一时刻只允许单线程操作.
发现了没 synchronized 万能有没有.这么牛掰自然是要付出代价的. synchronized 牺牲了性能保证了这么强大功能.所以在使用的时候,也是不能一切都加锁哦.毕竟加锁消耗性能,还有引入死锁等问题.比如无并行需要数据可见时,只需要 volatile ,不影响性能有满足要求.
写在文尾,队 JVM 有兴趣,推荐研读 <深入浅出java虚拟机>

关注订阅号程序猿小哈,联系作者,加入学习交流群和小伙伴们一起学习进步
ht
热门排行