8个指令:read/write、load/store、use、assign、lock/unlock
read | 将主存中的数据读取到CPU的高速缓存cache中 |
write | 将CPU的cache中数据写回到主存中 |
load | 将CPU的高速缓存cache中数据加载到JVM的寄存器中 |
store | 将JVM寄存器中的数据写入CPU的cache中 |
lock | 对数据进行加锁操作 |
unlock | 对数据进行解锁操作 |
assign | 接收到赋值指令时,给工作内存中的变量赋值 |
use | 数据被JVM进行运算操作 |
Java内存模型中有主内存、工作内存
对象放在堆内存中
主内存 线程对应自己的工作内存(CPU级别的缓存)
工作内存:寄存器、高速缓存、写缓冲区
public class HelloWord(){
private int data = 0;
public void increment(){
data++;
}
}
HelloWorld helloWorld = new HelloWorld();
//线程1
new Thread(){
public void run(){
helloWorld.increment();
}
}.start();
//线程2
new Thread(){
public void run(){
helloWorld.increment();
}
}.start();
上面这段代码在内存中的过程如下图所示
假设两个线程同时进行
(1)将data=0这个值从主内存中read出来
(2)将data=0这个值load到对应线程的工作内存中
(3)执行data++工作:从工作内存中将data=0值提出来,交给线程use执行data++操作
(4)线程执行完data++操作,将各自工作内存中的data值assgin设置为1
(5)将工作内存中的data=1store取出来
(6)尝试将data=1的值write写入主内存中
两个线程并发去更新data,可能会将data的值更新为1
【评论区】
1、java内存模型是对计算机的一个抽象,将整个计算过程分成了6步去执行。因为每个线程都对应一个工作内存,所以导致主存中的数据值可能并不是最新的,因此多线程情况下,data++的这个操作就会被覆盖掉。 为什么要有工作内存的?它带来了内存不可见性与伪共享这些问题。是因为CPU的速度远高于内存的读写速度,因此为了充分利用CPU资源,设计了对应的缓存,还有一些其他的加载机制。 避免内存不可见用volatile就行,但是volatile的语义并不能保证data++能达到预期效果,因为它没办法保证这个执行的原子性