Java高并发编程详解系列-十二

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/nihui123/article/details/90445686

volatile关键字介绍,要了解volatile需要了解的还有Java内存模型,以及CPU内存模型等知识。首先从CPU和Java内存模型开始说起。

CPU Cache模型

  在之前的时候,分享过一个博客一篇博客,就是关于介绍CPU与内存速度不匹配的问题,为了解决这个问题,在CPU和内存之间设计了高速缓存,这个高速缓存出现就是解决CPU和RAM主存之间的速度不匹配。现在CPU的缓存已经增加到了三层。其中最靠近CPU的缓存称为L1,然后依次是L2,L3和主内存。

在这里插入图片描述
  Cache的出现是为了解决CPU直接访问内存效率低下的问题,在程序运行的过程中会将运算需要的数据从主存中复制一份到CPUCache中,这样的话CPU就可以直接对CPUCache中的数据进行读写操作。计算结束之后将结果刷新到主存中,CPU直接访问Cache提高了访问的速度,提高提升了效率。

在这里插入图片描述

CPU缓存一致性问题

  缓存的出现提升了CPU的运算效率,同时也出现了缓存不一致的问题,例如i++操作,在程序运行的过程中,首先将主存中的数据复制一份到CPU Cache中,然后从CPU缓存进行读写操作。

  1. 读取 主存中的 i 到 Cache
  2. 对Cache中的 i 进行加 1 操作
  3. 然后将结果写回到Cache中
  4. 将结果重新刷新到主存中

这样的操作在单线程的情况下是没有任何问题的,但是在多线程的情况下就会出现问题,因为我们知道多线程每个线程都会有自己的工作内存(这个可以参考Java内存模型)对于变量 i 来说在每个线程的本地内存中都会有一个对应Cache缓存中的结果。这样多个线程同时访问就会出现缓存不一致的问题。为了解决这个问题提出了两种解决方案
1.通过总线加锁的方式
2.通过缓存一致性协议

第一种方式通常存在于CPU中,是一种悲观的方式实现,CPU和其他组件的通信都是通过总线来实现的(这个在计算机组成原理中有过对应的介绍)。如果采用总线的方式会一个组件进行操作,会阻塞其他组件的操作。
第二种方式是通过缓存一致性协议的方式来解决。
在这里插入图片描述
缓存一致性协议使用最多的就是Intel的MESI协议,MESI协议保证了每一个缓存中使用共享变量都是一致的。大致思想是当CPU在操作Cache中的数据时候,如果发现这个变量为一个共享变量,也就是说在其他的Cache中也存在一个副本。具体操作
1.读操作。不做任何处理,只将Cache中的数据读取到寄存器中
2.写入操作,发送信号通知其他CPU将变量Cache line设置为无效状态,其他的CPU进行操作的时候不得从主存中再次获取

Java内存模型

  Java内存模型(Java Memory Mode,JMM)指定了Java虚拟机如何使用计算机的主存进行工作。如下图,可以看到每个线程都会有自己对应的操作内存,当然从JDK1.5开始使用JVM对虚拟机内存进行了新的定义。Java内存模型也定义了线程与主存之间的抽象对应关系。

  1. 共享变量都存储在主内存中,可以被任意一个线程访问
  2. 每个线程都有私有的工作内存,本地内存
  3. 工作内存只存储该线程对应的共享变量副本
  4. 线程不会直接操作主内存,是通过直接写到线程本地内存中,然后进入主内存
  5. 线程的工作内存与虚拟机内存模型一样,是一个抽象的概念。并不是真实存在的。
    在这里插入图片描述

Java内存模型是一个抽象的概念,因为是个虚拟机的概念,所以与真实的硬件的内存并不完全一样,所以无论是JVM的堆内存和栈内存都会与计算机主内存进行对应。当然还有一部分的内存与Cache对应。如下图所示。

在这里插入图片描述

总结

  以上主要是对CPU内存模型和JVM内存模型做了一定的介绍。为使用volatile关键字提出了一个概念上的理解,相比较于之前的概念,这个概念是理解起来比较困难的。所以要理解起来还是需要一定的时间。但是真正理解了这个也是比较容易的,主要要对JVM内存模型有一定的了解。这里提供一个链接https://blog.csdn.net/nihui123/article/details/89527876,和一张图。有助于对本篇文章的理解。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/nihui123/article/details/90445686