本篇博客主要讲述并发编程中的一些基础内容, 并了解一下基本概念.
首先我们了解一下什么是并发?
同时拥有两个或者多个线程, 如果程序在单核处理器上运行, 多个线程将交替的换入或者换出内存, 这些线程是同时 "存在" 的, 每个线程都处于执行过程中的某个状态; 如果运行在多核处理器上, 此时, 程序中的每个线程都将分配到一个处理器核上, 因此可以同时运行.
什么又是高并发呢?
高并发是互联网分布式系统架构设计中必须考虑的因素之一, 它通常是指, 通过设计保证系统能够同时并行处理很多请求.
小结:
并发是多个线程操作相同的资源, 保证线程安全, 合理使用资源; 高并发是指服务能同时处理很多请求, 提高程序性能.
我们都知道 cpu 有多级缓存机制, 那么问题来了, 为什么需要 cpu cache?
cpu 的频率太快了, 快到主存跟不上, 这样在处理器时钟周期内, cpu 常常需要等待主存, 浪费资源. 所以 cache 的出现, 是为了缓解 cpu 和内存之间速度的不匹配问题
cpu 的多级缓存机制有两个显著的特点: 乱序执行优化和缓存一致性.
乱序执行优化是处理器为提高运算速度而做出违背代码原有顺序的优化. 例如我们呀计算 a=10; b=200; result=a*b; 实际执行的结果可能如下图所示
缓存一致性: 用于保证多个 cpu cache 之间缓存共享数据的一致, MESI 是缓存一致性协议中的一个, MESI 将 cache line(cache 与内存数据交换的最小单位) 的状态分为 modify,exclusive,shared,invalid, 分别是修改, 独占, 共享和失效.
modify: 当前 cpu cache 拥有最新数据, 其他 cpu 拥有失效数据, 虽然当前 cpu 中的数据和主存是不一致的, 但是以当前 cpu 的数据为准
exclusive: 只有当前 cpu 中有数据, 其他 cpu 中没有该数据, 当前 cpu 的数据和主存中的数据是一致的
shared: 当前 cpu 和其他 cpu 中都有共同数据, 并且和主存中的数据一致
invalid: 当前 cpu 中的数据失效, 数据应该从主存中获取, 其他 cpu 可能有数据也可能没有数据, 当前 cpu 的数据和主存被认为是不一致的; 对于 invalid 而言, 在 MESI 协议中采取的是写失效.
MESI 协议中, 每个 cache 的控制器不仅知道自己的操作 (local read 和 local write), 通过监听也知道其他 cpu 中的 cache 操作 (remote read 和 remote write), 对于自己本地缓存有的数据, cpu 仅需要发起 local 操作; 否则发起 remote 操作, 从主存中读取数据, cache 控制器通过总线监听, 仅能够知道其他 cpu 发起的 remote 操作, 但是如果 local 操作会导致数据不一致性, cache 控制器会通知其他 cpu 的 cache 控制器修改状态.
local read: 读本地 cache 中的数据
local write: 将数据写到本地 cache
remote read: 读取内存中的数据
remote write: 将数据写到主存.
MESI 协议中 cache line 的数据状态有四种, 引起数据状态转换的 cpu cache 的操作也有四种, 所以共有 16 中状态转换, 下面我们来简单看一个场景
最初的时候, 所有 cpu 都没有数据, 某一个 CPU 发生读操作, 此时发生 remote read 来读取内存中的数据, 数据从主存中读取到当前 CPU 的 cache, 状态为 E(只有当前 cpu 有数据, 且和主存一致), 此时如果有其他 CPU 也读取数据, 则状态修改为 S(共享, 多个 cpu 之间拥有相同的数据, 并且和主存保持一致), 如果其中某一个 CPU 发生数据修改, 那么该 CPU 中数据状态改为 M(拥有最新数据, 和主存不一致, 但是以单签 cpu 中的为准), 并通知其他拥有该数据的 cpu 数据失效, 其他 cpu 中的 cache line 状态修改为 I(失效, 和主存中的数据被认为不一致, 数据不可用应该重新获取)
注意: cpu 的 cache 控制器会监听总线上其他 CPU 的操作, 所以可以知道其他 CPU 的行为 (如其他 CPU 进行了 RR 等)
MESI 协议是为了保证多个 cpu cache 中共享数据的一致性.
java 内存模型和主机的内存是什么关系呢?
我们首先分别看一下, java 内存模型和主机内存模型:
根据 jvm 的基础知识我们可以知道, 堆区是线程共享的, 栈区是线程独占的.
其实 CPU 中是有寄存器的存在的, 寄存器拥有非常高的读写速度, 如下图所示:
我们 jvm 的内存与计算机的内存的对应关系如下 (堆和栈都是分布在主内存中):
有了对上图的理解, 我们来看下 java 内存模型
为了获取更好的性能虚拟机或硬件系统优先让工作内存存储于寄存器和高速缓存中, 本地内存是 java 内存模型的抽象的概念, 涵盖了寄存器告诉缓存等.
注意 java 内存模型和 jvm 内存模型的区别: java 内存模型中线程的工作内存是 cpu 的寄存器和高速缓存的一个抽象描述, jvm 内存模型是对内存的物理划分, 只局限在内存而且只局限在 jvm 的内存.
java 内存模型的同步操作与规则如下图所示:
lock(锁定): 作用于主内存的变量, 把一个变量标志为一个线程独占状态
unlock(解锁): 作用于主内存变量, 把一个处于锁定状态的变量释放出来, 释放后的变量才可以被其他线程锁定
read(读取): 作用于主内存的变量, 把一个变量从主内存传输到线程的工作内存中, 以便随后的 load 动作使用
load(载入): 作用于工作内存的变量, 它把 read 操作从主内存中得到的变量放入工作内存的变量副本中
use(使用): 作用于工作内存的变量, 把工作内存中的一个变量值传递给执行引擎
assign(赋值): 作用于工作内存的变量, 它把一个从执行引擎接收到的值赋值给工作内存的变量
store(存储): 作用于工作内存的变量, 把工作内存中一个变量的值传送给主内存中, 以便随后的 write 操作
write(写入): 作用于主内存的变量, 它把 store 操作从工作内存中一个变量的值传送到主内存的变量中
来源: https://www.cnblogs.com/sbrn/p/8971040.html