JMM及Volatile详解


Java内存模型

保证在多线程环境下变量的相互可见性与有序性,不保证原子性

 保证可见性:当某个线程修改volatile变量时,JMM会强制将这个修改更新到主内存中,并且让其他线程工作内存中存储的副本失效。

确实store在前,write在后

store与write反了,先store后write

 store与write反了,先store后write

 

因为++不是原子操作,有三步

 

 

 v前面的,没说到点子上哦。之所以小于理论值,是因为在线程回写数据到主内存中的时候,覆盖了已经被其他更快的线程执行的结果

线程1加一值为1但是线程二拿到的时候是0,加一还是1。所以两次操作值还是1,肯定就少了

通过例子来说明第一层语义,假设线程1先执行,线程2后执行:

//线程1
boolean stop = false;
while(!stop){
doSomething();
}
1
2
3
4
5
//线程2
stop = true;
1
2
针对这个程序存在以下的问题:每个线程在运行过程中都有自己的工作内存,那么线程1在运行的时候,会将stop变量的值拷贝一份放在自己的工作内存当中。那么当线程2更改了stop变量的值之后,但是还没来得及写入主存当中,线程2转去做其他事情了,那么线程1由于不知道线程2对stop变量的更改,因此还会一直循环下去。

但是用volatile修饰之后就变得不一样了:

首先使用volatile关键字会强制将修改的值立即写入主存;

之后使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效);

最后由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。

那么在线程2修改stop值时(当然这里包括2个操作,修改线程2工作内存中的值,然后将修改后的值写入内存),会使得线程1的工作内存中缓存变量stop的缓存行无效,然后线程1读取时,发现自己的缓存行无效,它会等待缓存行对应的主存地址被更新之后,然后去对应的主存读取最新的值。

那么线程1读取到的就是最新的正确的值。

但是Volatile关键字仍然不能保证原子性。

问题在于volatile关键字能保证可见性没有错,但是上面的程序错在没能保证原子性。可见性只能保证每次读取的是最新的值,但是volatile没办法保证对变量的操作的原子性。

在前面已经提到过,自增操作是不具备原子性的,它包括读取变量的原始值、进行加1操作、写入工作内存。那么就是说自增操作的三个子操作可能会分割开执行,就有可能导致缓存不同步的情况出现。
————————————————
版权声明:本文为CSDN博主「daijingxin」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/daijingxin/article/details/113767433