目录:
  1.重排序场景
  2.追根溯源
  3.缓存一致性协议
  4.重排序原因

一、重排序场景

class ResortDemo {
int a = 0;
boolean flag = false; public void writer() {
a = 1; //
flag = true; //
} Public void reader() {
if (flag) { //
int i = a * a; //
……
}
}
}

当两个线程 A 和 B,A 首先执行writer() 方法,随后 B 线程接着执行 reader() 方法。线程B在执行操作4时,能否看到线程 A 在操作1对共享变量 a 的写入?

答案是:不一定能看到。

由于操作1和操作2没有数据依赖关系,编译器和处理器可以对这两个操作重排序;同样,操作3和操作4没有数据依赖关系,编译器和处理器也可以对这两个操作重排序。

二、追根溯源

  为了提升计算性能,CPU 从单核升级到了多核甚至用到了超线程技术最大化提高 CPU 的处理性能。CPU增加了高速缓存,操作系统增加了进程、线程,通过CPU时间片的切换最大化的提升CPU的使用率。
 

  通过高速缓存的存储交互很好的解决了处理器与内存的速度矛盾,但是也为计算机系统带来了更高的复杂度,因为它引入了一个新的问题,缓存一致性。

三、缓存一致性协议

  同一份数据可能会被缓存到多个 CPU 中,如果在不同 CPU 中运行的不同线程看到同一份内存的缓存值不一样就会存在缓存不一致的问题。为了达到数据访问的一致,需要各个处理器在访问缓存时遵循一些协议,在读写时根据协议来操作,常见的协议有MSI,MESI,MOSI 等。最常见的就是 MESI 协议。

接下来
给大家简单讲解一下 MESI。
  MESI 表示缓存行的四种状态,分别是
  1. M(Modify) 表示共享数据只缓存在当前 CPU 缓存中,

并且是被修改状态,也就是缓存的数据和主内存中的数

据不一致

  2. E(Exclusive) 表示缓存的独占状态,数据只缓存在当前

CPU 缓存中,并且没有被修改
  3. S(Shared) 表示数据可能被多个 CPU 缓存,并且各个缓存中的数据和主内存数据一致
  4. I(Invalid) 表示缓存已经失效
 

                                             

  对于 MESI 协议,从 CPU 读写角度来说会遵循以下原则:
  CPU 读请求:缓存处于 M、E、S 状态都可以被读取,I 状态 CPU 只能从主存中读取数据。
  CPU 写请求:缓存处于 M、E 状态才可以被写。对于 S 状态的写,需要将其他 CPU 中缓存行置为无效才可写。

四、重排序原因

  MESI 协议虽然可以实现缓存的一致性,但是也会存在一些问题。
  

  基于上图中的原因,CPU又引入了storeBuffers的缓冲区。CPU0 只需要在写入共享数据时,直接把数据写入到 storebufferes 中,同时发送 invalidate 消息,然后继续去处理其

他指令。当收到其他所有 CPU 发送了 invalidate acknowledge 消息时,再将 store bufferes 中的数据数据存储至 cache line中。最后再从缓存行同步到主内存。        
                                                                  

    这个时候,我们再来看上述标题一中的重排序场景。

    class ResortDemo {
     int a = 0;
     boolean flag = false;      public void writer() {
     a = 1; //1
     flag = true; //2
     }      Public void reader() {
     if (flag) { //3
     int i = a * a; //4
……
     }
     }
    }
  当执行1操作时,a的状态从S->M,此时,线程A会先把变更写入到storebuffers,然后发送invalidate去异步通知其他CPU线程,紧接着就执行了下面的2操作。
此时,可能1的变更还在storebuffers中,并未提交到主内存。什么时候会提交到主内存,也不确定。
所以,线程B调用read方法可能会出现,看到了flag的变更,但是看不到a的变更,就出现了重排序的现象。

cpu指令重排序的原理的更多相关文章

  1. CPU指令重排序与MESI缓存一致性

    一.重排序场景 class ResortDemo { int a = 0; boolean flag = false; public void writer() { a = 1; //1 flag = ...

  2. java高并发核心要点|系列4|CPU内存指令重排序(Memory Reordering)

    今天,我们来学习另一个重要的概念. CPU内存指令重排序(Memory Reordering) 什么叫重排序? 重排序的背景 我们知道现代CPU的主频越来越高,与cache的交互次数也越来越多.当CP ...

  3. Java的多线程机制系列:不得不提的volatile及指令重排序(happen-before)

    一.不得不提的volatile volatile是个很老的关键字,几乎伴随着JDK的诞生而诞生,我们都知道这个关键字,但又不太清楚什么时候会使用它:我们在JDK及开源框架中随处可见这个关键字,但并发专 ...

  4. Java的多线程机制系列:(四)不得不提的volatile及指令重排序(happen-before)

    一.不得不提的volatile volatile是个很老的关键字,几乎伴随着JDK的诞生而诞生,我们都知道这个关键字,但又不太清楚什么时候会使用它:我们在JDK及开源框架中随处可见这个关键字,但并发专 ...

  5. 深入浅出Java并发包—指令重排序

    前面大致提到了JDK中的一些个原子类,也提到原子类是并发的基础,更提到所谓的线程安全,其实这些类或者并发包中的这么一些类,都是为了保证系统在运行时是线程安全的,那到底怎么样才算是线程安全呢? Java ...

  6. JVM并发机制的探讨——内存模型、内存可见性和指令重排序

    并发本来就是个有意思的问题,尤其是现在又流行这么一句话:“高帅富加机器,穷矮搓搞优化”. 从这句话可以看到,无论是高帅富还是穷矮搓都需要深入理解并发编程,高帅富加多了机器,需要协调多台机器或者多个CP ...

  7. 深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则

    转: http://www.blogjava.net/xylz/archive/2010/07/03/325168.html 在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到 ...

  8. 轻松学JVM(二)——内存模型、可见性、指令重排序

    上一篇我们介绍了JVM的基本运行流程以及内存结构,对JVM有了初步的认识,这篇文章我们将根据JVM的内存模型探索java当中变量的可见性以及不同的java指令在并发时可能发生的指令重排序的情况. 内存 ...

  9. 【java多线程系列】java内存模型与指令重排序

    在多线程编程中,需要处理两个最核心的问题,线程之间如何通信及线程之间如何同步,线程之间通信指的是线程之间通过何种机制交换信息,同步指的是如何控制不同线程之间操作发生的相对顺序.很多读者可能会说这还不简 ...

随机推荐

  1. .net core 使用swagger接口描述

    首先安装nuget包 Swashbuckle.AspNetCore.Swagger Swashbuckle.AspNetCore.SwaggerGen Swashbuckle.AspNetCore.S ...

  2. nodeJS从入门到进阶二(网络部分)

    一.网络服务器 1.http状态码 1xx: 表示普通请求,没有特殊含义 2xx:请求成功 200:请求成功 3xx:表示重定向 301 永久重定向 302 临时重定向 303 使用缓存(服务器没有更 ...

  3. node连接Mysql报错ER_NOT_SUPPORTED_AUTH_MODE

    报错信息 本人系统安装的是mysql-installer-community-8.0.18.0.msi这个版本,然后我本地使用node-mysql去连接数据库. test.js文件 var mysql ...

  4. ar 解压一个.a文件报错: xxx.a is a fat file (use libtool(1) or lipo(1) and ar(1) on it)

    Linux  使用终端指令 ar x /Users/apple/Desktop/libWC_LIB_SDKT.a解压一个文件 报错如图所示: 是因为该.a文件包含了多个cpu架构,比如armv7,ar ...

  5. Swagger Liunx环境搭建(亲测百分百可用)

    一.安装nodejs 下载编译好的nodejs安装包,下载地址: https://nodejs.org/dist/v10.10.0/ (作者下载的10.10.0,可根据自己需要下载不同版本) 将下载好 ...

  6. Linux安装和配置MySQL5.7【修改密码、修改字符集等配置】(5.7.18+版本也可参考,我是5.7.22)

    本文是转载篇,这里是 原文原文原文. --------------------分割线------------------------------- 这下面内容讲述如何修改密码为简单密码(比如:1234 ...

  7. 8 个 Python 实用脚本,赶紧收藏备用!

    脚本写的好,下班下得早!程序员的日常工作除了编写程序代码,还不可避免地需要处理相关的测试和验证工作. 例如,访问某个网站一直不通,需要确定此地址是否可访问,服务器返回什么,进而确定问题在于什么.完成这 ...

  8. Linux命令——jobs、bg、fg、nohup

    参考:Bash基础——工作管理(Job control) jobs -l :除了列出 job number 与命令串之外,同时列出 PID 的号码: -r :仅列出正在背景 run 的工作:-s :仅 ...

  9. 数据库开发-Django ORM的数据库迁移

    数据库开发-Django ORM的数据库迁移 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一. Django 项目准备 1>.安装django包 pip install d ...

  10. Python并发编程-concurrent包

    Python并发编程-concurrent包 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.concurrent.futures包概述 3.2版本引入的模块. 异步并行任务编程 ...