【JVM】内存模型与优化并发操作
JMM与内存交互
Java内存模型的主要目的是定义程序中各种变量的访问规则。Java内存模型的意义,在不同的平台上实现读写的一致性。尤其是对于并发问题。主内存与工作内存的交互。 主内存主要对应于Java堆中的对象实例数据部分,而工作内存则对应于虚拟机栈中的部分区域。
- Java内存模型规定了:
- 所有的变量都存储在主内存中。包括了实例字段、静态字段和构成数组对象的元素,但是不包括局部变量与方法参数,因为后者是线程私有的。
- 每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用的变量的主内存副本
- 线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的数据。
- 不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。
内存交互
JVM在实现时能够对一些操作保证原子性(8种):lock, unlock, read, use, load, assign, store, write.
很多操作具有成对性质且保证顺序,但是不必连续执行。线程不许丢弃最近的assign的操作,同样不许一个变量没有被assign就被传回。一个变量只能从主内存中诞生。对一个变量lock时,会清空工作内存中此内存的值,在unlock之前,需要把变量刷回主内存。
可见性、重排序、原子性
volatile关键字,这部分内容可以见并发内容的复习。
8条happens-before:
- 程序串行先后,本线程内操作有序。
- 锁先后
- 线程启动先后
- volatile规则
- 线程终止先后
- 线程中断先后
- 对象终结先后
- 传递性先后
线程安全
- 不可变时线程安全的
- 互斥同步时安全的,synchronized和reentrantlock
- 非互斥同步,乐观锁的方法。有冲突就回退。
锁优化
自旋锁与自适应自旋
- 自旋:如果线程可以很快获得锁,那么可以不挂起线程,而是让线程进行几个忙循环。节省线程挂起和切换的时间。
- 自适应自旋:自旋时间不再固定,而是由前一个在同一个锁的自旋时间和当前锁的状态绝对。
锁消除
-
在编译代码时候,检测到不存在数据竞争,JVM会锁消除。
-XX:+EliminteLocks
开启,并且需要逃逸分析-XX:+DoEscapeAnalisis
-
逃逸分析:方法中定义的对象可能别外部方法引用,称为方法逃逸;如果对象可能被外部线程访问,称为线程逃逸。如赋值给类变量或者可以在其他线程中仿真的实例变量。
锁粗化,轻量化锁,偏向锁
锁粗化:建议加到锁的同步范围扩大。合并多个synchronized
轻量化锁:优先使用乐观锁,如果失败升级为重量级锁。
偏向锁:在无竞争时,直接取消了锁。