章三 JMM
本文是JVM系列第三篇,主要描述java内存模型,包括原子操作、指令重排序、可见性、有序性等相关内容,是java并发编程核心原理与基础
<!-- TOC -->
<!-- TOC -->
JMM介绍
JMM是java提供的抽象模型,描述了在多线程环境中,主内存和工作内存的交互,主要目标是屏蔽硬件和操作系统的差异。
- 主内存:主内存用来存储所有共享变量
- 工作内存:线程独享内存,类似cpu缓存,线程从主内存读取共享变量到工作内存,或者将修改后的数据回写到主内存
内存模型特性
- 原子性:原子性表示一个操作不可分割,线程无法观察到其执行过程的中间态。例如基本变量的赋值(int a=10),通过synchronized或atomic实现的操作
- 可见性:可见性表示线程多共享变量的修改对其他线程可见,例如使用volatile、synchronized、final关键字修饰
- 有序性:有序性表示代码的执行顺序符合程序的逻辑顺序,但由于编译器和cpu的优化,实际执行顺序可能与代码顺序不一致(又称为指令重排序)
关键概念
主内存和工作内存的交互
- lock:作用于主内存,将变量标记为线程独占状态
- unlock:作用于主内存,解除某个内存地址的锁
- read:从主内存读取共享变量
- load:加载到工作内存
- use:工作内存中使用变量
- assign:修改变量
- store:变量回写到主内存
- write:将存储的值写回到主内存
补充说明: 1. lock和unlock通常是原子性操作 2. read/load和store/write:单次操作通常是原子性,例如32为的普通变量在jvm实现中读取和写入是原子操作。 64位的long和double类型变量在非volatile修饰是,read/write由于可能会分为两个32位块操作,导致非原子性 3. use/assign,对变量执行简单的赋值是原子型的,但 i++/i--非原子性 4. 多个内存组合操作通常不保证原子性 例如: int i=0; i++; 操作等价: a) read i:从主内存读取i b) load i:将i加载进工作内存 c) use i: 使用i值 d) assign i+i: 计算并重新赋值i==1 e) store i: 将新值存储到主内存 f) write i: 写会主内存
happens-before 原则:happens-before是jmm的核心规则,定义两个操作之间的顺序关系,如果A操作 happens-before B操作,那么A操作的结果对B操作可见
- 程序顺序原则:单线程中,前面的操作happens-before后面的操作
- 锁规则:一个程序释放锁,happens-before另一个线程获取锁
- volatile:对一个volatile变量的写操作happens-before之后的操作
- 线程启动规则:线程A调用线程B的start方法,happens-before线程B的执行
- 线程终止规则:线程B的终止happens-before线程A通过Thread.join()等方法获知线程B的终止
指令重排序
原因
- 编译器优化:提高代码执行效率
- 处理器优化:cpu使用乱序来提高流水线利用率
影响:指令重排序可能导致程序在多线程环境下执行结果与预期不同
class Singleton { private static Singleton instance; public static Singleton getInstance() { if (instance == null) { // 第一次检查 synchronized (Singleton.class) { if (instance == null) { // 第二次检查 instance = new Singleton(); } } } return instance; } }
instance = new Singleton() 包含以下步骤: 分配内存 初始化对象; 将引用指向对象。 重排序可能导致步骤 2 和步骤 3 交换,其他线程可能访问未完全初始化的对象。
volatile: java并发编程中的轻量同步机制,仅用来保证可见性和禁止重排序
内存屏障:
- 写屏障:在写操作前插入,确保写操作前的指令不被重排序到屏障后
- 度屏障:在读操作后插入,确保读操作后的指令不会重排序到屏障前
volatile保证线程从主内存读取最新值,并将修改立刻写回到主内存
- 普通变量:线程可能从工作内存中读取缓存的旧值
- volatile:线程总是从主内存读取最新值,避免数据不可见问题
局限:
- 不保证原子性:虽然保证了可见性,但不能保证复合操作的原子性,例如i++涉及多个内存操作
适用场景有限:只适用于状态标志、简单读写等场景,复杂线程安全问题仍需要synchronized或lock、Atomic
本文章同步发送到公众号‘编程智汇’,欢迎关注
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。