JMM详解:三大特征
accttodo 12/31/2023 Java
目录
参考:
# JMM的三大特征
整个 JMM 实际上是围绕着三个特征建立起来的。分别是:原子性,可见性,有序性。这三个特征可谓是整个 Java 并发的基础。
# 原子性
原子性指的是一个操作是不可分割,不可中断的,一个线程在执行时不会被其他线程干扰。
原子性:一个或多个操作,要么全部执行,要么全部不执行(执行的过程中是不会被任何因素打断的)。
我们来看下面的代码:
int i = 2;
int j = i;
i++;
i = i + 1;
1
2
3
4
2
3
4
上面这几句代码能保证原子性吗?
- 第一句是基本类型赋值操作,必定是原子性操作。
- 第二句先读取 i 的值,再赋值到 j,两步操作,不能保证原子性。
- 第三和第四句其实是等效的,先读取 i 的值,再 +1,最后赋值到 i,三步操作了,不能保证原子性。
JMM 只能保证基本的原子性,如果要保证一个代码块的原子性,提供了 monitorenter
和moniterexit
两个字节码指令,也就是 synchronized
关键字。因此在 synchronized
块之间的操作都是原子性的。
# 可见性
可见性指当一个线程修改共享变量的值,其他线程能够立即知道被修改了。
可见性:只要有一个线程对共享变量的值做了修改,其他线程都将马上收到通知,立即获得最新值。
Java 是利用 volatile
关键字来提供可见性的。 当变量被volatile
修饰时,这个变量被修改后会立刻刷新到主内存,当其它线程需要读取该变量时,会去主内存中读取新值。而普通变量则不能保证这一点。
除了 volatile
关键字之外,final
和 synchronized
也能实现可见性。
synchronized
的原理是,在执行完,进入unlock
之前,必须将共享变量同步到主内存中。final
修饰的字段,一旦初始化完成,如果没有对象逸出(指对象为初始化完成就可以被别的线程使用),那么对于其他线程都是可见的。
# 有序性
有序性: 有序性可以总结为:在本线程内观察,所有的操作都是有序的;而在一个线程内观察另一个线程,所有操作都是无序的。
在 Java 中,可以使用 synchronized
或者 volatile
保证多线程之间操作的有序性。实现原理有些区别:
volatile
关键字是使用内存屏障达到禁止指令重排序,以保证有序性。synchronized
的原理是,一个线程lock
之后,必须unlock
后,其他线程才可以重新lock
,使得被synchronized
包住的代码块在多线程之间是串行执行的。