三丶多线程的进阶二(volatile)

三丶多线程的进阶二(volatile)

1.volatile关键字的使用

volatile关键字,用在我们的变量前可以保证我们的变量的可见性(一个线程操作这个变量的时候,可以对其他的线程可见)。

2.volatile的原理

通过添加这个关键字的,可以是我们的数据在底层执行的指令的时候加了一个lock标志。

3.LOCK命令在底层的执行逻辑

1.首先需要了解一下我们的线程是如何内存交互的

这里可以看到,每个线程都会又有一个自己的高速缓存区,他们还会共享一个缓存区域,最后才会去修改主存。
2.lock锁其实就是告诉我们计算机底层去加锁,从而保证数据的安全性。但是锁有两种状态一个总线锁,另一个缓存锁。

总线锁:

总线锁就是锁住总线,当一个线程获取之后,其他线程都不能访问这个内存区域,知道当前线程释放锁,其他线程才能访问。(缺点就是消耗比较大)

缓存锁:

目的就是控制锁的缓存力度,通过保证缓存的一致性来解决。

两者优先使用缓存锁,但是有以下几种情况不能使用缓存锁:

①当前数据不能被缓存的当前线程。
②处理器不支持缓存锁定。

4.缓存一致性协议

常见的一致性协议有 MSI、MESI、MOSI 等。最常见的就是 MESI 协议。

4.1、MESI协议

M(Modify) 表示共享数据只缓存在当前 CPU 缓存中, 并且是被修改状态,也就是缓存的数据和主内存中的数据不一致。

E(Exclusive) 表示缓存的独占状态,数据只缓存在当前 CPU 缓存中,并且没有被修改。

S(Shared) 表示数据可能被多个 CPU 缓存,并且各个缓存中的数据和主内存数据一致。

I(Invalid) 表示缓存已经失效。

在 MESI 协议中,每个缓存的缓存控制器不仅知道自己的 读写操作,而且也监听其它 Cache 的读写操作。
对于 MESI 协议,从 CPU 读写角度来说会遵循以下原则:
CPU 读请求:缓存处于 M、E、S 状态都可以被读取,I 状 态 CPU 只能从主存中读取数据。
CPU 写请求:缓存处于 M、E 状态才可以被写。对于 S 状 态的写,需要将其他 CPU 中缓存行置变成I。

比如我们刚刚说的到这种情况:就会变成下面这样:
在这里插入图片描述

但是这样虽然保证了缓存的一致性,但是由于在线程1通知线程2之间,并且线程2没有回应成功之前,线程1一致处于阻塞状态。所以就引入一个store buffers 进行缓存,类似于一个异步的操作。

这样又带来一个新的问题,这样就会进行指令重排序。然后通过内存屏障解决了指令重排序。

内存屏障的三种类型:

在这里插入图片描述

JMM

JMM提供了多线程处理共享数据内存安全的规范。他是针对不同的操作系统的统一规范,类似jvm。
JMM是通过总线锁和缓存锁来进行底层 的处理,而对于指令重排序的问题,JMM在java层面上提供了volatile等关键字来保证禁止高速缓存,禁止指令重排序来处理。

总结

从计算机命令底层触发,通过总线锁和缓存锁保证数据的一致性,但是总线锁消耗过大,然后出现了缓存锁,缓存锁避免的线程等待引入了 store buffers进行异步,从而导致命令重排序,然后通过内存屏障来处理,保证数据的一致性。而JMM就为JAVA层面提供了volatile关键字来保证数据的可见性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值