JMM模型
该模型为抽象模型,并非物理模型。
工作流程
读:
从主内存中读取共享变量-> 拷贝一份到工作内存中 -> 交给cpu操作
写:
从主内存中读取共享变量 -> 拷贝一份到工作内存中 -> 交给cpu操作 -> 重新写回主内存。
可见性
即线程1和线程2如果同时访问了某个共享变量,如果线程1更改了该共享变量,线程2不一定能及时发现该变量被更改了。
可以通过加volatile关键字解决可见性问题,即如果某个共享变量被修改了,其他线程可以及时的知道该变量被修改了。
原子性
即要么全部执行完,要么不执行。
volatile不能保证原子性,哪怕给变量叫上volatile,也可能会出现线程不安全的情况。例如
public volatile static int count;
count++;
如果有多个线程同时执行count++操作,可能会丢失某几次的自增,这是因为count++的操作实际上分多步,不是原子的:
1. 读取count
2. count + 1;
3. 将count = count + 1
那么有可能线程A只完成了读取count,然后停住了,线程B完成了一次完整的+1操作,通知线程A重新去获取count的值,导致线程A的一次+1操作被丢失,结果少加了一次.
那么如何保证原子性呢?
用sychronized来给count++加锁。
有序性
指令重排
我们通常认为,代码会从上到下依次按顺序执行,但事实是,CPU会对指令进行优化,比如,
变量1 依赖 a
变量2 依赖 b
变量3 依赖 a
CPU发现变量1和3同时依赖a,那么会将3的代码提前,因为执行完1之后,a在内存中,马上去执行变量3的操作,会更快。
所以就会进行指令重排。但是,重排必须保证结果是正确的。
Volatile可以禁止指令重排优化,从而保证有序性,如果不能保证有序性,有可能会导致多线程下乱序执行的问题,从而导致出Bug。