1.在开始多线程之前,我们先来聊聊计算机的缓存
计算机处理一个程序需要cpu处理器与存储设备的交互。但是在计算机发展的过程中,cpu处理器的处理速度不断提高,而存储设备的读写速度却没有得到与cpu同样速度的发展。为了解决这个问题,我们在处理器与存储设备之间加了一层缓存,基本上解决了cpu与存储设备速度的差别。现在的计算机缓存大体上分为三级,L1级缓存,L2级缓存,L3级缓存,其中L1,L2级缓存属于每个cpu独享,L3级缓存属于多个cpu共享。但是这也引发了一个问题,就是对于共享数据操作的一致性问题。为了解决这个问题,在cpu与缓存之间,我们制定了缓存一致性协议,最常见的就是MESI协议。其中计算机cpu的缓存如下图所示。
解读:计算机处理器与主内存之间添加了一层缓存,为了保证各个处理器对于主内存中数据操作的安全性,引入了缓存一致性协议。各个处理器与主内存中数据的交互必须是建立在缓存一致性协议之上的。
2.理解JMM(java内存模型)
Java虚拟机试图定义一种java内存模型,从而屏蔽掉不同硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能够达到一致性访问的效果。为此,java定义了下面的java内存模型,如下图。
解读:从图中我们可以大概了解到java内存模型。首先是在每个单独的线程中有一个工作内存,各个工作内存互不干扰。每个线程操作主内存中的数据时,首先是从主内存中拷贝一份到工作内存(read和load操作,后面具体说明),然后对工作内存中的副本进行操作(use和assgin操作,后面具体说明),最后在将工作内存中的数据刷新到主内存中(store和write操作,后面具体说明)。以上就是java内存模型的大概操作流程,下面将详细介绍。
主内存与工作内存的交互:关于主内存和工作内存的交互,在java内存模型定义了8种原子操作,分别是lock,read,load,use,assign,store,write,unlock,下面逐一介绍下。
lock:表示锁定操作,作用于主内存中的变量,表示将一个变量标识线程独占状态,即一个线程标识为lock的变量,其它线程无法访问。
unlock:表示解锁操作,作用于主内存中的变量,表示将一个锁定的变量释放,只有被释放的变量才可被其它的线程访问。
read:表示读取操作,作用于主内存中的变量,表示将一个变量从主内存传输到工作内存。
load:表示载入操作,作用于工作内存中的变量,表示将read操作获取到的变量值存储到工作内存的变量副本之中。
use:表示使用操作,作用于工作内存中的变量,表示将工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用变量值的字节码指令时将会执行这个操作。
assgin:表示赋值操作,作用于工作内存中的变量,表示将一个从执行引擎获取到的值传递给工作内存中的变量,每当虚拟机遇到一个给变量赋值的字节码指令时将会执行这个操作。
store:表示存储操作,作用于工作内存中的变量,表示将一个工作内存中的变量值传递到主内存中,以方便随后的write操作。
write:表示写入操作,作用于主内存中的变量,表示将store操作从工作内存中传递的变量值存储到主内存中的变量中。
除了上述这八种操作之外,java内存模型定义了这八种操作的一些规则。
规则一:不允许一个变量从主内存中读取了,但工作内存不接收。也不允许一个变量从工作内存发起了回写,但主内存不接收。即read与load,store与write不许单独出现。
规则二:一个变量在工作内存中修改之后,必须同步回主内存。即assign操作之后,必须执行store和write操作。
规则三:一个变量在线程中没有执行assgin之前,不允许同步回主内存。
规则四:在一个线程中对变量执行use和store之前,必须先执行了assign和load操作。
规则五:lock和unlock操作规则,一个变量同一时刻只允许同一个线程对其执行lock操作,并且同一个线程可以对其执行多次lock操作。执行了多少次lock操作,释放这个变量的时候也必须执行多少次的unlock操作。
规则六:lock和unlock操作规则,对于执行了lock操作的一个变量,将会清空工作内存中这个变量的值,在执行引擎需要使用到这个变量的值时,需要先执行load或assign初始化这个变量的值。
规则七:lock和unlock操作规则,一个没有被执行lock操作的变量,不予许被执行unlock操作。一个线程对变量执行的lock操作,只能由当前这个线程对其执行unlock操作。
规则八:lock和unlock操作规则,一个变量被执行unlock之前,必须先将此变量同步回主内存,即执行store和write操作。
java多线程之volatile关键字