本篇博客主要讲述并发编程中的一些基础内容,并了解一下基本概念。
首先我们了解一下什么是并发?
同时拥有两个或者多个线程,如果程序在单核处理器上运行,多个线程将交替的换入或者换出内存,这些线程是同时“存在”的,每个线程都处于执行过程中的某个状态;如果运行在多核处理器上,此时,程序中的每个线程都将分配到一个处理器核上,因此可以同时运行。
什么又是高并发呢?
高并发是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。
小结:
并发是多个线程操作相同的资源,保证线程安全,合理使用资源;高并发是指服务能同时处理很多请求,提高程序性能。
我们都知道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操作从工作内存中一个变量的值传送到主内存的变量中