浅谈一下Java中的内存模型JMM
作者:刘婉晴
一、什么是 JMM
JMM,全程是 Java Memory Model ,直译就是 Java 内存模型。根据这个名字,可以知道它是 Java 设计用来管理内存的一个模型。
Java 中的内存分为主内存和本地内存,线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。下图很清晰地阐述了这个关系 :
因为 Java 通过共享内存作为线程间的通信机制,因此 JMM 和线程通信息息相关,可以说 JMM 控制线程的通信,具体来说 JMM 控制一个线程对共享变量的写入何时对另一个线程可见 。
二、 JMM 基础
本节介绍了解 JMM 需要的一些前置知识, 即和 JMM 有关的一些概念
2.1 Java 线程通信机制
线程通信机制一共有两种(共享内存 和 消息传递), Java 使用 共享内存模型 作为线程同步机制
2.2 同步
同步是指程序如何控制不同线程操作发生的相对顺序,在多线程并发情况下,同步是保证多线程下安全问题的非常重要的操作。
2.3 堆内存
Java 内存有栈内存、堆内存 和 方法区 。 堆内存中含有线程共享的 实例、静态变量 和 数组元素等 ,也就是说堆内存是线程共享区 。
2.4 指令重排
正常情况,按照我们的理解,我们写一段程序在处理器中就是按照我们写的顺序执行的 。
但是实际上,在我们看不到的地方,处理器为了提高执行效率,优化性能,会对指令进行重排 。
以下面的程序为例子,解释指令重排:
int a = 1; // .... 语句1 int b = 2; // ..... 语句 2
正常情况下, 语句 1 优先于 语句 2 执行 。
但是,处理器实际在执行时,会根据情况进行处理,可能会出现 语句2 优先与 语句1 执行的情况。
但是因为对程序的执行结果没有影响, 所以,我们程序员不知道发生了指令重排 。
在单线程情况下,指令重排不影响程序运行的结果。但是多线程情况下,可能会造成影响。
因此, JMM 通过插入 内存屏障 禁止特定类型的处理器重排序来保证多线程并发下的程序执行安全 。
2.5 顺序一致性模型
顺序一致性模型,是 JMM 为保证多线程下的安全而提出的,正确同步的程序应该具有顺序一致性,其具有如下特点:
按照程序顺序执行,不能重排序操作原子执行,对所有线程可见 2.6 同步程序
同步程序并不是完全按照顺序一致性执行,其在临界区内可以进行重排序,这样的设计给了处理器足够的优化空间,同时也没有改变程序的执行结果 。
三、volatile 关键字
3.1 volatile 做什么
volatile 相当于同一个锁对对该变量的读写操作进行了同步,用 volatile 修饰的变量总是能被同步到共享内存中,避免了因为本地内存的存在导致的线程不安全问题 。volatile 具有如下两个特性:
- 可见性:一个线程的读,总是能看到另一个线程的写
- 原子性: 对单个变量的读写具有原子性, 但是对复合操作不具有原子性
3.2 volatile 原理
3.2.1 规则
- 当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序
- 当第一个操作是volatile读时,不管第二个操作是什么,都不能重排序
- 当第一个操作是volatile写,第二个操作是volatile读时,不能重排序
3.2.2 实现
生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序
四、锁的内存语义
- 锁释放与volatile写有相同的内存语义
- 锁获取与volatile读有相同的内存语义
4.1 AQS Java同步器框架
AQS使用一个整型的volatile变量来维护同步状态, 实现 ReentrantLock
4.2 concurrent包
concurrent 包依赖于 CAS 和 volatile 实现
- 声明共享变量为volatile
- 使用CAS的原子条件更新来实现线程之间的同步
- 配合以 volatile 的读/写和 CAS 所具有的 volatile 读和写的内存语义来实现线程之间的通信
到此这篇关于浅谈一下Java中的内存模型JMM的文章就介绍到这了,更多相关Java内存模型JMM内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!