从缓存入门到并发编程三要素详解 Java中 volatile 、final 等关键字解析案例

引入高速缓存概念

  1. 在计算机在执行程序时,以指令为单位来执行,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入 。
  2. 由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行指令的速度很快,而从内存读取数据和向内存写入数据的过程相对很慢,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度 。因此就引入了高速缓存 。
  3. 特性:缓存(Cache memory)是硬盘控制器上的一块内存,是硬盘内部存储和外界接口之间的缓冲器 。
高速缓存作用呢?
  1. 预读取
    ?相当于提前加载,猜测你可能会用到硬盘相邻存储地址的数据,它会提前进行加载到缓存中,后面你需要时,CPU就不需要去硬盘读取数据,直接读取缓存中的数据传输到内存中就OK了,由于读取缓存的速度远远高于读取硬盘时磁头读写的速度,所以能够明显的改善性能 。
  2. 对写入动作进行缓存
    ?硬盘接到写入数据的指令之后,并不会马上将数据写入到盘片上,而是先暂时存储在缓存里,然后发送一个“数据已写入”的信号给系统,这时系统就会认为数据已经写入,并继续执行下面的工作,而硬盘则在空闲(不进行读取或写入的时候)时再将缓存中的数据写入到盘片上 。
  3. 【从缓存入门到并发编程三要素详解 Java中 volatile 、final 等关键字解析案例】换到应用程序层面也就是,当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据同步到主存当中 。
举个简单的例子,比如下面的这段代码:
i = i + 1;
  • 当线程执行这个语句时,会先从主存当中读取i的值,然后复制一份到高速缓存当中,然后CPU执行指令对i进行加1操作,然后将数据写入高速缓存,最后将高速缓存中i最新的值刷新到主存当中 。
  • 这个代码在单线程中运行是没有任何问题的,但是在多线程中运行就会有问题了(存在临界区) 。在多核CPU中,每条线程可能运行于不同的CPU中,因此每个线程运行时有自己的高速缓存区(对单核CPU来说,其实也会出现这种问题,只不过是以线程调度的形式来分别执行的) 。
比如有两个线程像下列执行顺序:
  1. 线程一执行 i = i + 1,线程二执行var = i
  2. 线程二此时去主存中获取变量 i,线程一只是在高速缓存中更新了变量,还未将变量i写会主存
  3. 线程二读到的i不是最新值,此时多线程导致数据不一致
?类似上面这种情况即为缓存一致性问题 。读写场景、双写场景都会存在缓存一致性问题,但读读不会 。前提是需要在多线程运行的环境下,并且需要多线程去访问同一个共享变量 。
?这里的共享又可以回到上文中,即为上面所说,他们每个线程都有自己的高速缓存区,但是都是从同一个主存同步获取变量 。
那么这种问题应该怎样解决呢?
解决缓存不一致问题(硬件层面)
  1. 总线加锁模式