JMM模型

image.png
该模型为抽象模型,并非物理模型。

工作流程

读:
从主内存中读取共享变量-> 拷贝一份到工作内存中 -> 交给cpu操作

写:
从主内存中读取共享变量 -> 拷贝一份到工作内存中 -> 交给cpu操作 -> 重新写回主内存。

可见性

即线程1和线程2如果同时访问了某个共享变量,如果线程1更改了该共享变量,线程2不一定能及时发现该变量被更改了。

可以通过加volatile关键字解决可见性问题,即如果某个共享变量被修改了,其他线程可以及时的知道该变量被修改了。

原子性

即要么全部执行完,要么不执行。

volatile不能保证原子性,哪怕给变量叫上volatile,也可能会出现线程不安全的情况。例如

  1. public volatile static int count;
  2. count++;

如果有多个线程同时执行count++操作,可能会丢失某几次的自增,这是因为count++的操作实际上分多步,不是原子的:

  1. 1. 读取count
  2. 2. count + 1;
  3. 3. count = count + 1

那么有可能线程A只完成了读取count,然后停住了,线程B完成了一次完整的+1操作,通知线程A重新去获取count的值,导致线程A的一次+1操作被丢失,结果少加了一次.

那么如何保证原子性呢?
用sychronized来给count++加锁。

有序性

指令重排

我们通常认为,代码会从上到下依次按顺序执行,但事实是,CPU会对指令进行优化,比如,

  1. 变量1 依赖 a
  2. 变量2 依赖 b
  3. 变量3 依赖 a

CPU发现变量1和3同时依赖a,那么会将3的代码提前,因为执行完1之后,a在内存中,马上去执行变量3的操作,会更快。

所以就会进行指令重排。但是,重排必须保证结果是正确的。

Volatile可以禁止指令重排优化,从而保证有序性,如果不能保证有序性,有可能会导致多线程下乱序执行的问题,从而导致出Bug。