1,多线程的三个特性?
- 原子性
即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执。
- 可见性
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
- 有序性
即程序执行的顺序按照代码的先后顺序执行
2,可见性问题的来源?
背景: 计算机核心组件:CPU、内存、I/O设备,三者在处理速度上存在巨大差异,CPU速度最快>内存>I/O设备(磁盘)。
为了提升计算性能,CPU从单核提升到了多核,甚至用到了超线程技术最大化提高CPU处理性能,如果后两者处理性能没有跟上,意味着整体的计算效率取决于最慢的设备。为了平衡三者之间的速度差异,最大化的利用CPU提升性能,从硬件、操作系统、编译器等方面,做出了很多优化:
【硬件层面优化】CPU增加高速缓存
【操作系统层面优化】操作系统增加了进程和线程:通过CPU的时间片切换最大化的提升CPU的使用率。
【编译器层面优化】编译器指令优化:更合理的去利用好CPU的高速缓存。
计算机中大部分运行任务不能只依靠CPU“计算”就能完成,CPU还需要与Memory交互,比如读取运算数据、存储运算结果,这个 I/O 操 作是很难消除的。
而由于计算机的存储设备与处理器的运算速度差距非常大,所以现代计算机系统都会增加一层读写速度尽可能接近处理器运算速度的高速缓存来作为内存 和处理器之间的缓冲:将运算需要使用的数据复制到缓存中,让运算能快速进行,当运算结束后再从CPU缓存同步到内存之中。
缓存一致性问题
每个 CPU 的处理过程是, 先将计算需要用到的数据缓存在 CPU 高速缓存中,在 CPU 进行计算时,直接从高速缓存中读取数据并且在计算完成之后写入到缓存中。在整个运算过程完成后,再把缓存中 的数据同步到主内存。
由于在多 CPU 种,每个线程可能会运行在不同的 CPU 内, 并且每个线程拥有自己的高速缓存。同一份数据可能会被缓存到多个 CPU 中,如果在不同 CPU 中运行的不同线程看到同一份内存的缓存值不一样就会存在缓存不一致的问题。
为了解决缓存不一致的问题,在 CPU 层面做了很多事情, 主要提供了两种解决办法:
解决方案一:总线锁
在多 cpu 下,当其中一个处理器要对共享内存进行操作的时候,在总线上发出一个 LOCK# 信号,这个信号使得其他处理器无法通过总线来访问到共享内存中的数据,总线锁定把 CPU 和内存之间的通信锁住了,这使得锁定期间,其他处理器不能操作其他内存地址的数据,总线锁定的开销比较大,这种机制显然是不合适的。多个CPU访问数据的时候互斥的问题,这样让CUP并行变为串行。
总线锁的优化的方式是控制锁的粒度
解决方案二:缓存锁
所谓缓存锁,就是指内存区域如果被缓存在处理器的缓存行中,并且在Lock期间被锁定,那么当它执行锁操作回写到内存时,不再总线上加锁,而是修改内部的内存地址,基于缓存一致性协议来保证操作的原子性。(相比总线锁,缓存锁即降低了锁的力度。核心机制是基于缓存一致性协议来实现的。)
4.缓存一致性协议
MSI ,MESI 、MOSI …
为了达到数据访问的一致,需要各个处理器在访问缓存时遵循一些协议,在读写时根据协议来操作,常见的协议有MSI,MESI,MOSI等。最常见的就是MESI协议。接下来给大家简单讲解一下MESI
MESI表示缓存行的四种状态,分别是
M(Modify) 表示共享数据只缓存在当前CPU缓存中,并且是被修改状态,也就是缓存的数据和主内存中的数据不一致