并发编程-锁的那些事儿【一:并发的本质-Java内存模型】

前言

时隔几周,今天我们正式进入并发编程的艺术之中, 在此我分享下指引我学习方向的书籍:《深入理解Java虚拟机》,《Java并发编程的艺术》,《Java并发编程实战》 按照上述顺序依次阅读;

真正开始接入并发这门艺术时,其实说难也不难,说简单也不简单。 对于已经“进去”人,他们可以抓住主要核心思想去理解,那么对于“没进去”的小白,可能会感觉这个玩意该从哪找切入点学习呢? ---- 没关系,本次我来会分享我学习时的一些方向和心得。

面对这种平常挂在嘴上说,但有摸不清门路的东西。 我通常会从书中获得切入点。 同样的带着问题去看书:
1、并发的本质原理是啥?
2、用什么样的技术可以实现并发?

并发的本质原理是啥?

想要搞清楚并发,那么就得知道其本质。 上述提的三本书中都有说到。 其中都会提到一个名词 “Java内存模型”,那么这个又是啥呢?

先来了解下 最早之前,物理计算机的原理; 当电脑还是单核,运算量不大时。 计算一组公式的运算有CPU去完成,计算出来的产物和参与计算的参数,则需要放置在内存中。那么其中就多了CPU与内存之间的I/O操作。 由于计算机的存储设备与CPU运算速度,没在一个等级上,比如 cpu一秒钟可以计算1w个公式,内存储存速度只能达到5千,那cpu总不能等着内存慢慢存储吧,效率很低。所以不得不在cpu与内存之间加了一层高速缓存,俗称寄存器,用来作为cpu与内存之间的缓冲。 将运算的参数先放到寄存器中,那么cpu就可以不用等待,等运算结束后,在将寄存器的数据同步到内存中。

于此同时也会造成一个问题:缓存一致性 随着发展,多核时代进入,每个cpu都有自己的寄存器,但都共享同一个主内存。那么当多个cpu对同一块主内存的区域进行运算时,那么该以谁为准呢? so 为了解决这一问题,就得需要有一些规范来约束了。在进行读写时,必须按照规范进行操作。 由此咱们就引出 并发编程第一个重点: Java内存模型

Java内存模型 [在特定的规范下,对特定的内存或工作内存进行读写操作的抽象过程]

在接触Java开发时,总所周知的一个特性就是:Java可以无视平台化,进行运行。其答案就在于这个内存模型中。 此模型的诞生,就是为了来屏蔽各个硬件间与操作系统的 内存访问差异。 譬如C、C++他们则是使用的物理内存模型,那么就带来一个问题:换一台不同的模型,那么这套程序就会出问题了,因为内存访问方式变化了,随着而来就是应用也得跟着变化。

Java内存模型主要定义应用中各个变量的访问规则;即如何在内存中对变量进行储存和访问。
而且规定所有的变量均要存储在主内存【类比物理内存】中,每个线程会有私有的工作内存【类比寄存器】,工作内存红保存了主内存中被该线程使用到的变量副本。线程在对其进行操作时,都必须在各自的工作内存中,不能直接读取主内存。 同时每个线程之间工作内存是独立,不能相互访问。 so 这里就引出了 线程 工作内存 主内存三个名称,用下面图来看下交互关系吧
并发编程-锁的那些事儿【一:并发的本质-Java内存模型】_第1张图片

是否会有人问 这套模型与JVM的模型有啥关系么? 实际来说,其实没太大关联。 死气白咧要扯关系的话, 主内存---->Java堆的对象实例化这部分、工作内存----->虚拟栈的部分区域;

内存之间访问规则

上诉介绍了Java内存模型的构造,也提了有特定规范来约束存储与访问的,那这篇详细说明下:

模型中定义了**8种原子操作**:

  • lock(锁定): 作用于主内存,它把一个变量标记为一条线程独占状态;
  • unlock(解锁):作用于主内存,它将一个处于锁定状态的变量释放出来,释放后的变量才能够被其他线程锁定;
  • read(读取):作用于主内存,它把变量值从主内存传送到线程的工作内存中,以便随后的load动作使用;
  • load(载入):作用于工作内存,它把read操作的值放入工作内存中的变量副本中;
  • use(使用):作用于工作内存,它把工作内存中的值传递给执行引擎,每当虚拟机遇到一个需要使用这个变量的指令时候,将会执行这个动作;
  • assign(赋值):作用于工作内存,它把从执行引擎获取的值赋值给工作内存中的变量,每当虚拟机遇到一个给变量赋值的指令时候,执行该操作;
  • store(存储):作用于工作内存,它把工作内存中的一个变量传送给主内存中,以备随后的write操作使用;
  • write(写入):作用于主内存,它把store传送值放到主内存中的变量中。

从上面规则来解答下一下2个问题:

  1. 如何将变量从主内存复制到工作内存?
    需要顺序的经过read与load操作。

  2. 如何将变量从工作内存同步到主内存?
    需要顺序的经过store与write操作。

发现了么? 这个俩组操作缺一不可,并且一定得按照顺序执行,但没有规定得连续执行,咋理解呢? 例如 现在主内存有 a b俩个变量, 需要copy到工作内存: read a ,read b ,load b,load a。

知道了有这8种原子操作后,也定义了**8条操作需要满足的规则**:

  • 不允许read和load、store和write操作之一单独出现,以上两个操作必须按顺序执行,但没有保证必须连续执行,也就是说,read与load之间、store与write之间是可插入其他指令的。
  • 不允许一个线程丢弃它的最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存。
  • 不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存中。
  • 一个新的变量只能从主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量,换句话说就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。
  • 一个变量在同一个时刻只允许一条线程对其执行lock操作,但lock操作可以被同一个条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。
  • 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。
  • 如果一个变量实现没有被lock操作锁定,则不允许对它执行unlock操作,也不允许去unlock一个被其他线程锁定的变量。
  • 对一个变量执行unlock操作之前,必须先把此变量同步回主内存(执行store和write操作)。

总结下: 那么并发的本质到底是啥呢? 其实很明白了,保证缓存的一致性, 并发说到底也就是,1-N[线程] 1对多的内存关系时,如何能保证数据的准确,有条有序的执行。 Java内存模型只是帮助其定义一套规范,在实际开发中的操作都得基于这套规范上来操作。 原理理解后,还需看看用什么样的技术方式,实现上述的规则。 也就是下面要说的并发三大特性;

你可能感兴趣的:(并发编程,并发编程,JUC,Java锁)