深入理解 Java 虚拟机 (二)
Java内存模型
主内存与工作内存
- 所有的变量存储在主内存(虚拟机内存的一部分)
- 每条线程有自己的工作内存,线程对变量的所有操作(读取、赋值)都必须在工作内存中进行、不能直接读写主内存中的数据。
- 不同的线程无法直接访问对方工作内存中的变量。
内存间交互操作
- 在
Java
中,内存之间的交互操作通常是通过对象的引用来实现的。当一个对象被创建时,它被存储在堆内存中,并且在栈内存中分配一个引用指向该对象。通过这个引用,可以访问和操作对象的数据和方法。
8种基本操作
-
lock
:锁定,作用于主内存变量,它把一个变量标识为一条线程独占的状态。 -
unlock
:解锁,解锁后的变量才能被其他线程锁定。 -
read
:读取,作用于主内存变量,它把一个主内存变量的值,读取到工作内存中。 -
load
:载入,作用于工作内存变量,它把read
读取的值,放到工作内存的变量副本中。 -
use
:使用,作用于工作内存变量,它把工作内存变量的值传递给执行引擎,当JVM
遇到一个变量读取指令就会执行这个操作。 -
assign
:赋值,作用于工作内存变量,它把一个从执行引擎接收到的值赋值给工作内存变量。 -
store
:存储,作用域工作内存变量,它把工作内存变量值传送到主内存中。 -
write
:写入,作用于主内存变量,它把store
从工作内存中得到的变量值写入到主内存变量中。
原子性可见性与有序性
原子性
- 原子性是指一个操作或者多个操作要么全部执行并且对外部可见,要么都不执行,不会出现中间状态。
- 在
Java
中,通过synchronized
关键字和Lock
接口来实现原子性操作。
可见性
- 可见性是指一个线程对共享变量的修改能够及时地被其他线程观察到。
- 在
Java
中,通过volatile
关键字和synchronized
关键字来实现可见性。 volatile
关键字可以保证共享变量的可见性,但不能保证原子性;synchronized
关键字可以保证共享变量的原子性和可见性。
有序性
- 有序性是指一个线程中的操作按照程序代码的先后顺序执行,并且不会被重排序。
- 在
Java
中,由于处理器、编译器和虚拟机等因素的影响,程序代码中的操作可能会被重排序,从而导致多线程程序出现不可预期的结果。 - 为了保证有序性,
Java
提供了happens-before
规则,用于定义操作之间的执行顺序。例如,一个unlock
操作happens-before
于后续对同一个锁的lock
操作。
锁优化
自旋锁
- 自旋锁(
Spin Lock
)是一种基本的锁实现方式,它是一种忙等待的锁,线程在获取锁时不会被挂起,而是循环检测锁的状态直到获取到锁为止。 - 自旋锁适用于临界区代码执行时间非常短暂的情况,避免线程频繁地切换和阻塞,提高了锁的性能。