多线程并发知识点整理(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38292691/article/details/86666819

2-1 CPU多级缓存-缓存一致性

M:(modified)被修改、  该缓存行只被缓存在该CPU的缓存中,并且是被修改过的。因此他与主存中的数据是不一致的。该缓存行中的内存,需要在未来的某个时间点 写回主存。这个时间点,我们是允许其他CPU读取主存中相应的内存之前,当这里面的值被写回主存之后呢!该缓存行的状态变为E(独享)状态

E、(enclusive)独享 独享状态的缓存行,只被缓存在该CPU的缓存中,他是未被修改过的,是与主存中的数据一致的,这个状态可以在任何时刻,当有其他CPU在读取该内存时,变成S(共享)状态。同样的当CPU修改 该缓存行的内容时,该状态可以变为M(修改状态)

S:(shared)共享 共享状态意味着该缓存行 ,可能被多个CPU进行缓存,并且各个缓存中的数据,与主存中的数据是一致的。当有一个CPU修改该缓存行的时候。其他CPU中该缓存行是可以被作废的。变成invalid状态

I:(Invalid)无效的 无效状态,代表该缓存是无效的。可能是有其他CPU修改了该缓存行。

Local Read:代表的是读本地缓存中的数据

Local Write:代表的是将数据写到本地的缓存里面

Remote Read:代表的是将内存中的数据读取过来

Remote Write:是讲数据写回到主存里面去

MESI的协议数据抓状态有四种,引起这四种状态变化的操作也是有四种(状态之间的相互转换关系,可以使用下图表示:)

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

比如Local Read和Remote Read除了在 I 状态下,M、E、S的状态都是可以进行读取的。一个写请求只有在该缓存行是M状态或者是E状态下,才能够被执行。如果当前状态是处于S状态的时候呢!他必须先将缓存中的该缓存行,变成无效的状态,这个操作通常使用广播的方式来完成。这个时候,他既不允许不通的CPU同时修改同一个缓存行,即使修改该缓存行不同位置的数据也是不允许的,这里主要解决的是缓存一致性的问题。

一个处于 M 状态的缓存行,必须时刻监听所有试图读该缓存行,相对于主存的操作。这种操作,必须在缓存行写回到主存,并将状态变成S状态之前,被延迟执行。

一个处于S状态的缓存行,也必须监听其他缓存,使该缓存行无效或者独享该缓存行的请求,并将缓存行变成无效。

一个处于E状态的缓存行,他要监听 其他缓存读缓存中,该缓存行的操作。一旦有该缓存行的操作,那么他需要变成S状态。

因此对于M和E两种状态而言,他的数据总是精确的。他们和缓存行的真正状态,他是一致的,而S状态可能是非一致的。

如果一个缓存将处于S状态的缓存行作废了,另一个缓存实际上可能已经独享了该缓存行,但是该缓存,却不会讲缓存行升迁为E状态,这是因为其他缓存,不会广播他们做废掉该缓存行的通知,同样,由于缓存并没有保存该缓存的Topic的数量,因此也没有办法确定自己是否已经独享了该缓存行。

从上面的描述来看呢,这个 E 状态更像是一种投机性的优化。因为如果一个CPU想要修改一个处于 S的共享状态的缓存行,总线事物需要讲所有该缓存行Topic的值变成Invalid状态才可以。而修改 E状态的缓存,他却不需要使用总线事物。

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

在单核时代,处理器的乱序执行优化 不会导致处理结果远离语气目标,但是多核环境下,却并非如此。首先在多核环境下,同时会有多个核执行指令,每个核的指令都可能被乱序。另外处理器还引入了L1、L2等缓存机制,每个核都有自己的缓存。这就导致了逻辑上,后写入的数据,未必真的最后写入。最终带来了另外一个问题。如果我们不做任何防护措施,处理器最终得出的结果和我们的逻辑结果大不相同。

比如我们在一个核上,执行数据写入操作。并在最后写一个标记,用来表示之前的数据已经准备好了,然后从另外一个核上通过判断这个标记来判定所需要的数据是否已经就绪。这种做法就存在一定的风险。标记先被写入,但是之前的操作却并未完成。这个未完成,可能是没有计算完成,也有可能是数据没有从处理器缓存刷新到主存当中,最终导致了另外一个核使用了错误的数据。

2-3 JAVA内存模型 

JMM(JAVA MEMORY MODEL)为了屏蔽掉各种硬件和操作系统访问内存之间的差异,已实现JAVA程序在各种平台下都能保持一致的并发效果

改模型规范了JAVA 虚拟机与计算机内存是如何协同工作的,它规定了一个线程是如何和何时可以看到其他线程修改过的共享变量的值,以及在必须时,如何同步的访问共享变量

JAVA的堆呢,是一个运行的数据区,堆是由垃圾回收来负责的。堆的优势呢,是可以动态的分配内存的大小。生存期也不必事先告诉编译器(以为他是在运行时,动态分配内存的)JAVA的垃圾回收器,会自动的收集这些不在使用的数据。但是也有缺点,由于在运行时动态分配内存,因此他的存取速度相对的较慢一些。

JAVA的栈,栈的优势,存取速度相对于堆的速度要快,仅次于计算机的寄存器,栈的数据是可以共享的,他的缺点呢,是存在栈中的数据的大小与生存期必须是确定的,缺乏灵活性。栈中存取一些基本类型的变量,比如byte、short、int...   JMM要求 调用栈和本地变量 存放在线程栈上, 对象存放在 堆 上。一个本地变量可能是指向一个对象的引用,这种情况下“引用”这个本地变量是存放在线程栈上,但是对象本身存放在堆上。一个对象,她可能包含方法Method,这些方法可能包含本地变量,这些本地变量的引用任然是存放在线程栈上的,即使这些方法所属的对象存放在堆上。一个对象的成员变量,可能会随着这个对象的自身存放在堆上。不管这个成员变量是原始类型还是引用类型。静态成员变量跟随着类的定义,一起存放在堆上。存放在堆上的对象可以被准时对这个对象引用的线程访问。

当一个线程可以访问这个对象的时候呢,他也可以访问这个对象的成员变量。如果两个线程同时调用同一个对象上的同一个方法,他们将会都访问这个对象的成员变量。这时每一个线程都拥有了这个对象的私有拷贝。

2-4并发的优势与风险

猜你喜欢

转载自blog.csdn.net/qq_38292691/article/details/86666819