【并发编程-基础】(一)并发编程基础

一、并发与并行

1.1、并发

       程序同时拥有两个或多个线程,如果程序在单核处理器上运行,多个线程将交替地换入或者换出内存,这些线程是同时存在的,每个线程都处于执行过程中的某个状态。如果程序运行在多核处理器上,程序中的每个线程都将分配到一个处理器核上,此时多个线程可以同时运行。

1.2、并行

       两个或多个事件在同一时刻发生。

1.3、高并发

       High Concurrency 是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指通过设计保证系统能够同时并行处理很多请求。

二、CPU多级缓存CPU cache

2.1、为什么需要CPU cache?

       CPU的频率太快了,快到主存跟不上,这样在处理器的时钟周期内,CPU常常需要等待主存,浪费资源。所以cache的出现,是为了缓解CPU和内存之间速度的不匹配问题(结构:CPU->cache->memory)。

2.2、CPU cache有什么意义?

       缓存容量远远小于主存

2.3、局部性原理

       (1) 事件局部性:如果某个数据被访问,那在不久的将来它很可能被再次访问。

       (2) 空间局部性:如果某个数据被访问,那么与它相邻的数据很快也可能被访问。

三、CPU缓存一致性(MESI Modify|Exclusive|Share|Invalid)

3.1、缓存一致性的内容:

       为了保证多个CPU cache之间缓存共享的一致性,定义了cache的四种状态,而CPU对cache的四种操作可能会产生不一致的状态,因此缓存控制器监听到本地操作和远程操作的时候,需要对地址一定的catline状态做出一定的修改从而保证数据在多个缓存之间流动的一致性。

要理解缓存一致性就需要将下面这4*4种状态及状态的转换搞清楚。
在这里插入图片描述

3.2、四种状态:MESI是四种状态的缩写。
  • M(修改):Modify被修改,该缓存行只被缓存在该CPU的缓存中,并且是被修改过的,因此它与主存中的数据是不一致的,该缓存行中的内存需要在未来的某个时间点写回主存,这个时间点允许其它CPU读取主存中相应的内存之前,当这里面的值被写回主存之后,该缓存行的状态会变成E状态(独享)。
  • E(独享):Exclusive独享,缓存行只被缓存到该CPU的缓存中,它是未被修改过的,是与主存中的数据一致的,这个状态会在任何时刻,当有其它CPU读取该内存时变成共享状态S状态。当CPU修改该缓存行的内容时,该状态会变成M修改状态。
  • S(共享) :Share共享,该缓存行可能被多个CPU进行缓存,并且各缓存中的数据与主存数据是一致的,当有一个CPU修改该缓存行的时候,其它CPU中该缓存行是会被作废的。变成I无效状态
  • I(无效的):Invalid无效的,无效的时候代表这个缓存是无效的,可能是有其它CPU使用了该缓存行。
3.3、四种操作
  • ​ local read:读本地缓存中的数据
  • ​ local write:将数据写在本地的缓存里面
  • ​ remote read:将内存中的数据读取过来
  • ​ remote write:将数据写回到主存里面去
3.3、缓存在各个状态上的切换及情况

       在一个典型的多核系统中,每一个核都会有一个自己的缓存来共享主存总线,每一个相应的CPU都会发起读写请求,而缓存的目的是为了减少CPU读写共享请求的次数,一个缓存除了在E状态中,都能满足读请求

       M和E的数据总是精确的,和缓存行的真正状态是一致的。S状态可能是不一致的,如果一个缓存将处于S状态的缓存行作废了,另一个缓存实际上可能已经独享了该缓存行,但该缓存行却不会将缓存行新签为E状态,这是因为其它缓存不会广播它作废掉缓存行的通知,同样由于缓存并没有保存该缓存行的token的数量,因此也没有方法确定是否已经独享了该缓存行。E像一种投机性的优化。

3.4、CPU多级缓存-乱序执行优化

在这里插入图片描述

(1)单核情况不会让执行情况远离目标。在多核时代,同时会有多个核执行指令,每个核的指令都可能被乱序。
(2)另外处理器还引入了一些缓存机制,每个核都有自己的缓存,就导致了逻辑顺序上后写入内存的数据,最后未必真的写入。
(3)最终带来的问题是如果不做任何防护措施,处理器最终得出的结果和逻辑得出的结果大不相同。

四、Java内存模型

4.1、Java内存模型抽象结构图

在这里插入图片描述

4.2、Java内存模型-同步操作与规则

在这里插入图片描述
同步规则:

  1. 如果要把一个变量从主内存中复制到工作内存,就需要按顺序地执行store和write操作。但Java内存模型只要求上诉操作必须按顺序执行,而没有保证必须是连续执行。
  2. 不允许read和load、store和write操作之一单独出现。
  3. 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中。
  4. 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中。
  5. 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。
  6. 一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条直线重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。lock和unlock必须成对出现。
  7. 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值。
  8. 如果一个变量实现没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。
  9. 对一个变量执行unclock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。

五、并发的优势与风险

5.1、并发的风险
  • 安全性:多个线程共享数据时可能会产生于期望不相符的结果
  • 活跃性:某个操作无法继续进行下去时,就会发生活跃性问题。比如死锁、饥饿问题
  • 性能:线程过多时会使得CPU频繁切换,调度时间增多;同步机制;消耗过多内存。
5.2、并发的优势
  • 速度:同时处理多个请求,响应更快;复杂的操作可以分成多个进程同时进行。
  • 设计:程序设计在某些情况下更简单,也可以有更多选择
  • 资源利用:CPU能够在等待IO的时候做一些其他的事情
发布了20 篇原创文章 · 获赞 1 · 访问量 566

猜你喜欢

转载自blog.csdn.net/weixin_42295814/article/details/103765953