JAVA线程内存模型
计算机指令都是在CPU中执行的,在指令执行过程中必定伴随着大量的读取和写入操作,那个程序运行时的数据时存放在内存的,随着cpu的更新换代,内存的速度已经远远的跟不上cpu的速度,这使得cpu的读取写入效率非常低。为了解决这一问题,在cpu与内存间都以cpu高速缓存作为中转。也就是在cpu在运行指令时,将主存中的数据复制到cpu高速缓存中,随指令运算完毕后,将结果刷新回主内存。
多线程数据不一致性
在多线程中,每个线程都会将共享变量从主内存复制一份到自己的工作内存,当线程1进行数据变更时,变更的则是自己工作内存中的共享变量副本,而不是主内存的共享变量,此时线程2中并不会感知到线程1的变化,这就是多线程时产生的数据不一致,即多线程时的数据不可见性。
Volatile关键字
- volatile的原子操作:
read(读取):从主内存读取数据
load(载入):将主内存读取的数据写入到工作内存
use(使用):从工作内存读取数据来计算
assign(赋值):将计算好的值重新赋值到工作内存
store(存储):将工作内存数据写入主内存
write(写入):将store过去的变量值赋值给主内存中的变量
lock(锁定):将主内存变量加锁,标识为线程独占状态
unlock(解锁):将主内存中的变量解锁,解锁后其他线程可以对其变量进行锁定 - volatile缓存可见性的实现原理:
底层实现主要是通过汇编lock前缀指令,它会锁定这块内存区域的缓存回写到主内存。
当共享变量通过volatile修饰时,会开启MESI缓存一致性协议以及cpu嗅探机制。
MESI缓存一致性协议:多个cpu在主内存读取数据到各自的高速缓存中,当其中一个cpu修改了缓存中的数据,该数据会立马写回主内存,其他cpu通过cpu嗅探机制可以感知到数据的变化从而将各自的高速缓存中的数据设置为失效,再从主内存中重新读取数据。
注意的是,当其中一个线程的共享变量副本进行改变时,在进行store操作前,会为主内存加锁,同时其他cpu的cpu嗅探机制已经感知到共享变量的变更,则会将各自的共享变量副本设置为无效,并重新向主内存获取共享变量,但此时主内存还是处于锁定状态故而必须在此等待,等待修改变量的线程进行write操作将数据写入主内存后,才会释放锁,此时其他cpu才能够读取到主内存的最新值。