本文我将和大家讨论并发编程中最基础的一项技术:内存屏障或内存栅栏,也就是让一个CPU处理单元中的内存状态对其它处理单元可见的一项技术。

CPU使用了很多优化技术来实现一个目标:CPU执行单元的速度要远超主存访问速度。在上一篇文章 “Write Combing (合并写)”中我已经介绍了其中的一项技术。CPU避免内存访问延迟最常见的技术是将指令管道化,然后尽量重排这些管道的执行以最大化利用缓存,从而把因为缓存未命中引起的延迟降到最小。

当一个程序执行时,只要最终的结果是一样的,指令是否被重排并不重要。例如,在一个循环里,如果循环体内没用到这个计数器,循环的计数器什么时候更新(在循环开始,中间还是最后)并不重要。编译器和CPU可以自由的重排指令以最佳的利用CPU,只要下一次循环前更新该计数器即可。并且在循环执行中,这个变量可能一直存在寄存器上,并没有被推到缓存或主存,这样这个变量对其他CPU来说一直都是不可见的。

CPU核内部包含了多个执行单元。例如,现代Intel CPU包含了6个执行单元,可以组合进行算术运算,逻辑条件判断及内存操作。每个执行单元可以执行上述任务的某种组合。这些执行单元是并行执行的,这样指令也就是在并行执行。但如果站在另一个CPU角度看,这也就产生了程序顺序的另一种不确定性。

最后,当一个缓存失效发生时,现代CPU可以先假设一个内存载入的值并根据这个假设值继续执行,直到内存载入返回确切的值。

CPU核

|

V

寄存器

|

V

执行单元 -> Load/Store缓冲区->L1 Cache --->L3 Cache-->内存控制器-->主存

| |

+-> Write Combine缓冲区->L2 Cache ---+

代码顺序并不是真正的执行顺序,只要有空间提高性能,CPU和编译器可以进行各种优化。缓存和主存的读取会利用load, store和write-combining缓冲区来缓冲和重排。这些缓冲区是查找速度很快的关联队列,当一个后来发生的load需要读取上一个store的值,而该值还没有到达缓存,查找是必需的,上图描绘的是一个简化的现代多核CPU,从上图可以看出执行单元可以利用本地寄存器和缓冲区来管理和缓存子系统的交互。

在多线程环境里需要使用某种技术来使程序结果尽快可见。这篇文章里我不会涉及到 Cache Conherence 的概念。请先假定一个事实:一旦内存数据被推送到缓存,就会有消息协议来确保所有的缓存会对所有的共享数据同步并保持一致。这个使内存数据对CPU核可见的技术被称为内存屏障或内存栅栏。

内存屏障提供了两个功能。首先,它们通过确保从另一个CPU来看屏障的两边的所有指令都是正确的程序顺序,而保持程序顺序的外部可见性;其次它们可以实现内存数据可见性,确保内存数据会同步到CPU缓存子系统。

大多数的内存屏障都是复杂的话题。在不同的CPU架构上内存屏障的实现非常不一样。相对来说Intel CPU的强内存模型比DEC Alpha的弱复杂内存模型(缓存不仅分层了,还分区了)更简单。因为x86处理器是在多线程编程中最常见的,下面我尽量用x86的架构来阐述。

Store Barrier

Store屏障,是x86的”sfence“指令,强制所有在store屏障指令之前的store指令,都在该store屏障指令执行之前被执行,并把store缓冲区的数据都刷到CPU缓存。这会使得程序状态对其它CPU可见,这样其它CPU可以根据需要介入。一个实际的好例子是Disruptor中的BatchEventProcessor。当序列Sequence被一个消费者更新时,其它消费者(Consumers)和生产者(Producers)知道该消费者的进度,因此可以采取合适的动作。所以屏障之前发生的内存更新都可见了。

private volatile long sequence = RingBuffer.INITIAL_CURSOR_VALUE;

// from inside the run() method

T event = null;

long nextSequence = sequence.get() + 1L;

while (running)

{

try

{

final long availableSequence = barrier.waitFor(nextSequence);

while (nextSequence <= availableSequence)

{

event = ringBuffer.get(nextSequence);

boolean endOfBatch = nextSequence == availableSequence;

eventHandler.onEvent(event, nextSequence, endOfBatch);

nextSequence++;

}

sequence.set(nextSequence - 1L);

// store barrier inserted here !!!

}

catch (final Exception ex)

{

exceptionHandler.handle(ex, nextSequence, event);

sequence.set(nextSequence);

// store barrier inserted here !!!

nextSequence++;

}

}

Load Barrier

Load屏障,是x86上的”ifence“指令,强制所有在load屏障指令之后的load指令,都在该load屏障指令执行之后被执行,并且一直等到load缓冲区被该CPU读完才能执行之后的load指令。这使得从其它CPU暴露出来的程序状态对该CPU可见,这之后CPU可以进行后续处理。一个好例子是上面的BatchEventProcessor的sequence对象是放在屏障后被生产者或消费者使用。

Full Barrier

Full屏障,是x86上的”mfence“指令,复合了load和save屏障的功能。

Java内存模型

Java内存模型中volatile变量在写操作之后会插入一个store屏障,在读操作之前会插入一个load屏障。一个类的final字段会在初始化后插入一个store屏障,来确保final字段在构造函数初始化完成并可被使用时可见。

原子指令和Software Locks

原子指令,如x86上的”lock …” 指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨多个CPU。Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持程序顺序。

内存屏障的性能影响

内存屏障阻碍了CPU采用优化技术来降低内存操作延迟,必须考虑因此带来的性能损失。为了达到最佳性能,最好是把要解决的问题模块化,这样处理器可以按单元执行任务,然后在任务单元的边界放上所有需要的内存屏障。采用这个方法可以让处理器不受限的执行一个任务单元。合理的内存屏障组合还有一个好处是:缓冲区在第一次被刷后开销会减少,因为再填充改缓冲区不需要额外工作了。

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 内存屏障

java内存屏障_内存屏障 | 并发编程网 – ifeve.com相关推荐

  1. 并发编程网 - ifeve.com

    并发编程网: http://ifeve.com/tag/nio/

  2. java对象头_我的并发编程(二):java对象头以及synchronized升级过程

    一.概述 研究java对象头的目的是详细分析Java的synchronized锁的升级过程,因为synchronized在锁升级的时候,就是依赖对象头的信息来决定的.本博文针对64位的操作系统来对Ja ...

  3. gava java_guava | 并发编程网 – ifeve.com

    原文链接 译文链接 译者:沈义扬,校对:丁一 注意事项 截至JDK7,Java中也只能通过笨拙冗长的匿名类来达到近似函数式编程的效果.预计JDK8中会有所改变,但Guava现在就想给JDK5以上用户提 ...

  4. 并发运行linux,linux | 并发编程网 – ifeve.com

    JVM中有这样一段注释: // The base-class, PlatformEvent, is platform-specific while the ParkEvent is // platfo ...

  5. 内存屏障(Memory Barriers/Fences) - 并发编程中最基础的一项技术

    内存屏障(Memory Barriers/Fences) - 并发编程中最基础的一项技术_chuhan0449的博客-CSDN博客 我们经常都听到并发编程,但很多人都被其高大上的感觉迷惑而停留在知道听 ...

  6. java内存栅栏_内存屏障(Memory Barriers/Fences) - 并发编程中最基础的一项技术

    我们经常都听到并发编程,但很多人都被其高大上的感觉迷惑而停留在知道听说这一层面,下面我们就来讨论并发编程中最基础的一项技术:内存屏障或内存栅栏,也就是让一个CPU处理单元中的内存状态对其它处理单元可见 ...

  7. java 线程 并发_多线程高并发编程总结

    多线程 第一章 一.终止线程的三种方法: 1.使用退出标志,是县城正常退出,也就是当run方法完成后线程终止. 2.stop不推荐 3.使用interrupt(打了一个停止标记,并不是真的停止线程). ...

  8. java 读取excel2007 内存不足_内存不足错误 – 写入Excel时的Java堆空间

    我有近100,000条记录的数据,我正在尝试使用XSSFWorkbook通过 Java代码将数据写入.xlsx文件.我能够将数据库中的所有数据提取到ArrayList.通过迭代ArryList,我将数 ...

  9. java 内存池_内存池技术介绍(图文并茂,非常清楚)

    看到一篇关于内存池技术的介绍文章,受益匪浅,转贴至此. 6.1 自定义内存池性能优化的原理 如前所述,读者已经了解到"堆"和"栈"的区别.而在编程实践中,不可避 ...

最新文章

  1. bash 判断 os 版本_Kali Linux 2020.3开始用ZSH取代Bash旅程
  2. 计算机工程类高级职称,2019年工程类中高级职称都有哪些专业?
  3. DL之DNN:自定义2层神经网络TwoLayerNet模型(封装为层级结构)利用MNIST数据集进行训练、预测
  4. java groovyshell_在java中使用groovy怎么搞
  5. vector利用swap()函数进行内存的释放
  6. android 网络程序下载,Android之网络文件下载
  7. JQ实现仿淘宝条件筛选
  8. 【模块】【通信】---http模块中req和res 常用的属性介绍
  9. CAN总线与CANOPEN协议
  10. dir_recurse是 php函数,dir_recurse是一个函数么?
  11. ip ,子网掩码, 网关 ,主机位数,网络位数,子网数
  12. 一篇学完:王道考研408计算机网络(全)
  13. android代码 发警报音,Android 8中的警报重复
  14. 解决:uni-app 图片加载不出来
  15. 【考试记录】Apsara Clouder基础技能认证:阿里巴巴编码规范(Java)
  16. centos 7+ssr傻瓜式安装(仅供学习使用)
  17. 期货反向跟单--其实已经很快了
  18. 保研之旅9:东南大学“通信与信息系统学科”推免面试
  19. AssertionError 的来源
  20. 悦诗风吟网络营销的目标_“悦诗风吟”品牌的促销策略研究

热门文章

  1. 双模5G+5000毫安大电池+44w快充,vivo z6成年轻人性能之选
  2. Oracle数据库--Oracle作业基础知识整合
  3. 服务器状态显示异常,App常见异常状态
  4. requestLayout in layout问题
  5. light动名词_初中英语语法学习:动名词专项训练
  6. 那些停课的日子 by yjjr
  7. 【网工搬砖】光纤、光模块及光接口常用知识
  8. 服务器攻击常见的手段有哪些
  9. html 填满父容器,CSS让子元素div的高度填满父容器的剩余空间的方法
  10. CodeSmith for MySQL template