并发编程学习笔记(八、volitile)

目录:

  • 计算机硬件系统架构演进(缓存一致性问题)
    • 为何演进,如何演进
    • 演进后导致了什么问题
    • 如何解决
  • JVM如何解决缓存一致性问题
    • 如何解决
    • volatile内存语意含义
    • volatile原理

计算机硬件系统架构演进(缓存一致性问题)

1、为何演进,如何演进。

计算机在运行程序的时,每条指令都是在CPU中执行,在执行的过程中势必会涉及到数据的读写。

而程序运行的数据时存储在主内存中的,也就是我们常说的内存,那么此时就会有一个问题:

  • CPU执行速度过快,内存执行速度较之较慢
  • 若任何交互都需要与主内存打交道的话,这会大大降低CPU执行的效率

解决办法:

  • CPU高速缓存诞生。
  • CPU高速缓存只为某个CPU独有,只与在该CPU运行的线程有关。

这样CPU不必与执行速度较之较慢的主内存打交道,而是和运行速度快的CPU高速缓存打交道,这样充分利用了CPU的高效性能。

———————————————————————————————————————————————————————

2、演进后导致了什么问题,如何解决的

硬件的演进使用了CPU高速缓存,这在单线程中的确是没问题的,但在多线程中便会导致缓存一致性的问题。

i++;这段代码:

  • 先从主内存中取出i的值。
  • 然后复制一份到CPU高速缓存中,CPU执行+1操作。
  • 再然后将+1后的结果写回CPU高速缓存。
  • 最后刷新到主内存中。

此时有两个线程都这样操作,那预期i的值应该是3。

两个线程并发操作,会导致如下结果:

  • 两个线程分别从主内存中读取i的值到各自的高速缓存中。
  • 此时线程A先执行完,将结果2更新到主内存中。
  • 然后线程B后执行完,又将主内存中的值更新为2.
  • 所有当A、B两个线程执行完后,结果并不为3,而是2。

———————————————————————————————————————————————————————

3、如何解决

  • 主线上加锁。
  • 缓存一致性协议。

加锁的方式通过独占来实现,只有一个CPU能执行,效率极为低下,不推荐使用。所以一般会采用缓存一致性协议来实现,【窥探技术+MESI协议】。

窥探技术

  • 既然多核CPU发生缓存一致性的问题是不能共享主内存拿到的数据,那么我就共享数据。
  • 将所有内存传输都放到一条共享总线上,所有CPU都能看到这条总线上的数据。
  • 并且谁修改了后都通知其它CPU。

MESI协议

  • M:modify(修改),E:exclusive(独占),S:shared(共享),I:invalid(失效)。
  • 当缓存处于E或M时,CPU才能去写。
  • 如果CPU要执行写操作的话,需要向总线发送一条我要独占的请求,此时会通知其它CPU,你们的缓存是I(invalid)状态了。

JVM如何解决缓存一致性问题

因JVM是硬件层次之上的,所以需要解决缓存一致性问题。

1、如何解决:通过volatile关键字。

  • volatile的作用是保证共享变量的可见性不能保证原子性也不能保证线程安全
  • volatile的作用是确保所有线程同一时刻读取到的共享变量的值是一致的
  • 如果某个线程对volatile修饰的共享变量进行更新,那么其他线程可以立刻看到这个更新。

———————————————————————————————————————————————————————

2、volatile内存语意含义

volatile可以保证线程可见性且提供了一定的有序性,但是无法保证原子性。

在JVM底层volatile是采用内存屏障来实现的。

上面那段话,有两层语义

  • 保证可见性、不保证原子性(再次重声,仅仅保证可见性)。
  • 禁止指令重排序。

———————————————————————————————————————————————————————

3、volatile原理

JVM底层是通过一个叫做内存屏障的东西来完成。

内存屏障,也叫做内存栅栏,是一组处理器指令,用于实现对内存操作的顺序限制。

下面是完成上述规要求的一些规则,在NO的地方就是需要用内存屏障来控制的。

也就是第一个操作以volatile读开始的都要经过内存屏障,第二个操作是volatile写的时候都要经过内存屏障;第一个是volatile写, 第二个是volatile读也需要经过内存屏障。

读:Load,写:Store;再配合上图就很好记了。

原文地址:https://www.cnblogs.com/bzfsdr/p/12496280.html