Java内存模型Cookbook(二)内存屏障

Java多线程内存读写 —— 内存屏障的理解 在现代计算机中,CPU往往都是多核的,而由于每个CPU Core中都有自己的高速缓存Cache,因此就会造成内存数据读写的不一致性,表现为指令乱序与不可见性问题。为此,java为了统一物理世界中的计算机组成架构,提出了JMM内存模型,并抽象了LoadLoad,StoreStore,LoadStore,StoreLoad四个内存屏障指令来应对不同CPU的体系。本文先介绍下多核CPU体系下,并发编程需要克服的问题,然后再介绍下Java的内存屏障各自的含义,并举例说明对应的场景。 阅读详情

Java内存模型Cookbook(二)内存屏障

原文:http://gee.cs.oswego.edu/dl/jmm/cookbook.html 第二节

作者:Doug Lea 翻译:潘曦   校对:方腾飞

  1. 指令重排
  2. 内存屏障
  3. 多处理器
  4. 指南

编译器和处理器必须同时遵守重排规则。由于单核处理器能确保与“顺序执行”相同的一致性,所以在单核处理器上并不需要专门做什么处理,就可以保证正确的执行顺序。但在多核处理器上通常需要使用内存屏障指令来确保这种一致性。即使编译器优化掉了一个字段访问(例如,因为一个读入的值未被使用),这种情况下还是需要产生内存屏障,就好像这个访问仍然需要保护。(可以参考下面的优化掉内存屏障的章节)。

内存屏障仅仅与内存模型中“获取”、“释放”这些高层次概念有间接的关系。内存屏障并不是“同步屏障”,内存屏障也与在一些垃圾回收机制中“写屏障(write barriers)”的概念无关。内存屏障指令仅仅直接控制CPU与其缓存之间,CPU与其准备将数据写入主存或者写入等待读取、预测指令执行的缓冲中的写缓冲之间的相互操作。这些操作可能导致缓冲、主内存和其他处理器做进一步的交互。但在JAVA内存模型规范中,没有强制处理器之间的交互方式,只要数据最终变为全局可用,就是说在所有处理器中可见,并当这些数据可见时可以获取它们。

 

内存屏障的种类

几乎所有的处理器至少支持一种粗粒度的屏障指令,通常被称为“栅栏(Fence)”,它保证在栅栏前初始化的load和store指令,能够严格有序的在栅栏后的load和store指令之前执行。无论在何种处理器上,这几乎都是最耗时的操作之一(与原子指令差不多,甚至更消耗资源),所以大部分处理器支持更细粒度的屏障指令。

内存屏障的一个特性是将它们运用于内存之间的访问。尽管在一些处理器上有一些名为屏障的指令,但是正确的/最好的屏障使用取决于内存访问的类型。下面是一些屏障指令的通常分类,正好它们可以对应上常用处理器上的特定指令(有时这些指令不会导致操作)。

LoadLoad 屏障

序列:Load1,Loadload,Load2

确保Load1所要读入的数据能够在被Load2和后续的load指令访问前读入。通常能执行预加载指令或/和支持乱序处理的处理器中需要显式声明Loadload屏障,因为在这些处理器中正在等待的加载指令能够绕过正在等待存储的指令。 而对于总是能保证处理顺序的处理器上,设置该屏障相当于无操作。

StoreStore  屏障

序列:Store1,StoreStore,Store2

确保Store1的数据在Store2以及后续Store指令操作相关数据之前对其它处理器可见(例如向主存刷新数据)。通常情况下,如果处理器不能保证从写缓冲或/和缓存向其它处理器和主存中按顺序刷新数据,那么它需要使用StoreStore屏障。

LoadStore 屏障

序列: Load1; LoadStore; Store2

确保Load1的数据在Store2和后续Store指令被刷新之前读取。在等待Store指令可以越过loads指令的乱序处理器上需要使用LoadStore屏障。

StoreLoad Barriers

序列: Store1; StoreLoad; Load2

确保Store1的数据在被Load2和后续的Load指令读取之前对其他处理器可见。StoreLoad屏障可以防止一个后续的load指令 不正确的使用了Store1的数据,而不是另一个处理器在相同内存位置写入一个新数据。正因为如此,所以在下面所讨论的处理器为了在屏障前读取同样内存位置存过的数据,必须使用一个StoreLoad屏障将存储指令和后续的加载指令分开。Storeload屏障在几乎所有的现代多处理器中都需要使用,但通常它的开销也是最昂贵的。它们昂贵的部分原因是它们必须关闭通常的略过缓存直接从写缓冲区读取数据的机制。这可能通过让一个缓冲区进行充分刷新(flush),以及其他延迟的方式来实现。

在下面讨论的所有处理器中,执行StoreLoad的指令也会同时获得其他三种屏障的效果。所以StoreLoad可以作为最通用的(但通常也是最耗性能)的一种Fence。(这是经验得出的结论,并不是必然)。反之不成立,为了达到StoreLoad的效果而组合使用其他屏障并不常见。

下表显示这些屏障如何符合JSR-133排序规则。

需要的屏障 第二步
第一步Normal LoadNormal StoreVolatile Load
MonitorEnter
Volatile Store
MonitorExit
Normal Load   LoadStore
Normal Store   StoreStore
Volatile Load
MonitorEnter
LoadLoadLoadStoreLoadLoadLoadStore
Volatile Store
MonitorExit
  StoreLoadStoreStore

另外,特殊的final字段规则在下列代码中需要一个StoreStore屏障

x.finalField = v; StoreStore; sharedRef = x;

如下例子解释如何放置屏障:

class X {
	int a, b;
	volatile int v, u;

	void f() {
		int i, j;

		i = a;// load a
		j = b;// load b
		i = v;// load v
		// LoadLoad
		j = u;// load u
		// LoadStore
		a = i;// store a
		b = j;// store b
		// StoreStore
		v = i;// store v
		// StoreStore
		u = j;// store u
		// StoreLoad
		i = u;// load u
		// LoadLoad
		// LoadStore
		j = b;// load b
		a = i;// store a
	}
}

数据依赖和屏障

一些处理器为了保证依赖指令的交互次序需要使用LoadLoad和LoadStore屏障。在一些(大部分)处理器中,一个load指令或者一个依赖于之前加载值的store指令被处理器排序,并不需要一个显式的屏障。这通常发生于两种情况,间接取值(indirection):

Load x; Load x.field

和条件控制(control)

Load x; if (predicate(x)) Load or Store y;

但特别的是不遵循间接排序的处理器,需要为final字段设置屏障,使它能通过共享引用访问最初的引用。

x = sharedRef; … ; LoadLoad; i = x.finalField;

相反的,如下讨论,确定遵循数据依赖的处理器,提供了几个优化掉LoadLoad和LoadStore屏障指令的机会。(尽管如此,在任何处理器上,对于StoreLoad屏障不会自动清除依赖关系)。

与原子指令交互

屏障在不同处理器上还需要与MonitorEnter和MonitorExit实现交互。锁或者解锁通常必须使用原子条件更新操作CompareAndSwap(CAS)指令或者LoadLinked/StoreConditional (LL/SC),就如执行一个volatile store之后紧跟volatile load的语义一样。CAS或者LL/SC能够满足最小功能,一些处理器还提供其他的原子操作(如,一个无条件交换),这在某些时候它可以替代或者与原子条件更新操作结合使用。

在所有处理器中,原子操作可以避免在正被读取/更新的内存位置进行写后读(read-after-write)。(否则标准的循环直到成功的结构体(loop-until-success )没有办法正常工作)。但处理器在是否为原子操作提供比隐式的StoreLoad更一般的屏障特性上表现不同。一些处理器上这些指令可以为MonitorEnter/Exit原生的生成屏障;其它的处理器中一部分或者全部屏障必须显式的指定。

为了分清这些影响,我们必须把Volatiles和Monitors分开:

需要的屏障 第二步
第一步Normal LoadNormal StoreVolatile LoadVolatile StoreMonitorEnterMonitorExit
Normal Load   LoadStore LoadStore
Normal Store   StoreStore StoreExit
Volatile LoadLoadLoadLoadStoreLoadLoadLoadStoreLoadEnterLoadExit
Volatile Store  StoreLoadStoreStoreStoreEnterStoreExit
MonitorEnterEnterLoadEnterStoreEnterLoadEnterStoreEnterEnterEnterExit
MonitorExit  ExitLoadExitStoreExitEnterExitExit

另外,特殊的final字段规则需要一个StoreLoad屏障。

x.finalField = v; StoreStore; sharedRef = x;

在这张表里,”Enter”与”Load”相同,”Exit”与”Store”相同,除非被原子指令的使用和特性覆盖。特别是:

  • EnterLoad 在进入任何需要执行Load指令的同步块/方法时都需要。这与LoadLoad相同,除非在MonitorEnter时候使用了原子指令并且它本身提供一个至少有LoadLoad属性的屏障,如果是这种情况,相当于没有操作。
  • StoreExit在退出任何执行store指令的同步方法块时候都需要。这与StoreStore一致,除非MonitorExit使用原子操作,并且提供了一个至少有StoreStore属性的屏障,如果是这种情况,相当于没有操作。
  • ExitEnter和StoreLoad一样,除非MonitorExit使用了原子指令,并且/或者MonitorEnter至少提供一种屏障,该屏障具有StoreLoad的属性,如果是这种情况,相当于没有操作。

在编译时不起作用或者导致处理器上不产生操作的指令比较特殊。例如,当没有交替的load和store指令时,EnterEnter用于分离嵌套的MonitorEnter。下面这个例子说明如何使用这些指令类型:

class X {
	int a;
	volatile int v;

	void f() {
		int i;
		synchronized (this) { // enter EnterLoad EnterStore
			i = a;// load a
			a = i;// store a
		}// LoadExit StoreExit exit ExitEnter

		synchronized (this) {// enter ExitEnter
			synchronized (this) {// enter
			}// EnterExit exit
		}// ExitExit exit ExitEnter ExitLoad

		i = v;// load v

		synchronized (this) {// LoadEnter enter
		} // exit ExitEnter ExitStore

		v = i; // store v
		synchronized (this) { // StoreEnter enter
		} // EnterExit exit
	}

}

Java层次的对原子条件更新的操作将在JDK1.5中发布(JSR-166),因此编译器需要发布相应的代码,综合使用上表中使用MonitorEnter和MonitorExit的方式,——从语义上说,有时在实践中,这些Java中的原子更新操作,就如同他们都被锁所包围一样。

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

金蝶云星空-协同开发云-安装开发环境 金蝶协同开发云就是不需要每一个开发都在自己的本机安装开发环境,自己安装可容易出错,包括源代码管理也一并解决了,大大减少了开发人员的工作量和降低了上手的难度。 阅读详情

相关推荐

三角洲行动交易行自动化脚本

DFMarketBot - 三角洲行动自动交易助手 DFMarketBot是一款专为三角洲行动游戏设计的交易行自动化工具,通过OCR识别和鼠标模拟实现自动补卡、补子弹功能。项目采用Python开发,支持Windows平台,主要功能包括: 自动检测并购买指定物品(房卡/子弹) 价格监控与智能购买(低于设定价格自动下单) 自定义购买数量与次数限制 支持窗口/无窗口两种游戏模式 使用前需安装Tesseract OCR引擎并配置游戏为1920*1080分辨率。通过JSON文件可灵活配置监控物品清单、理想价格和购买策

愤怒的阿木木的博客 3万+

Java内存屏障

java内存屏障 1.1 什么是内存屏障(Memory Barrier)? 内存屏障(memory barrier)是一个CPU指令。基本上,它是这样一条指令: a) 确保一些特定操作执行的顺序; b) 影响一些数据的可见性(可能是某些指令执行后的结果)。编译器和CPU可以在保证输出结果一样的情况下对指令重排序,使性能得到优化。插入一个内存屏障,相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。内存屏障另一个作用是强制更新一次不同CPU的缓存。例如,一个写屏障会把这个屏障前写入的数

流浪Gypsy的博客 3931

Python XML 转 JSON,XML 转字典

今天我们将学习如何在 Python 中将 XML 转换为 JSON 和 XML 转换为字典。我们可以使用 Python 的xmltodict模块来读取 XML 文件并将其转换为字典或 JSON 数据。我们还可以在大型 XML 文件上进行流式处理,并将其转换为字典。在进入编码部分之前,让我们首先了解为什么需要进行 XML 转换。

rubys_的专栏 2501

x86 架构下 StoreLoad 屏障

从图例可以看到第六步assert失败的核心原因在于,cpu0上缓存过b,所以cpu0执行b = 1k立马写入cache,然后线程B所在的cpu1执行while(b==0)迅速跳出,然后执行 a = 1,由于此时 a 还在cpu0的store buffer中,所以导致assert失败。CPU1 执行assert( a == 1),发现a 不在cache中,向CPU0发送read消息,CPU0的cache中存在a,所以CPU1获取到a = 1,assert成功。1,为什么CPU要乱序执行,难道是考虑性能吗?

yiyeguzhou100的专栏 2156

新手必看:LiteRadio 2c SE遥控器从开箱到飞行的完整指南(附常见问题解答)

本文为新手提供LiteRadio 2c SE遥控器的完整使用指南,涵盖从开箱到首飞的20个关键步骤。详细介绍了遥控器的功能设置、与无人机的对频技巧以及首飞注意事项,帮助用户快速掌握飞行控制,避免常见错误。特别适合无人机初学者阅读。

weixin_29209801的博客 379

Unity 数字孪生实战:PiXYZ Plugin工业模型高效处理指南

本文是Unity数字孪生实战指南,重点介绍了如何使用PiXYZ Plugin高效处理工业CAD模型。针对工业模型导入Unity时面临的格式壁垒、数据爆炸和资产混乱等核心痛点,文章提供了从环境安装、基础导入到智能减面、资产清理的完整解决方案,并分享了预设自动化、运行时导入等高级技巧与性能优化策略,帮助开发者快速打通工业设计与实时交互流程。

weixin_29009501的博客 255

Java里面的四种内存屏障

volatile四种内存屏障

qq_48403611的博客 1118

深水区探秘:小红书App x-s参数逆向与高并发爬虫实战(2026版)

在移动互联网的碎片化时代,小红书凭借其“社区+电商”的独特模式,积累了超过3亿月活用户。其内容生态覆盖了从美妆、穿搭到旅行、数码等几乎所有垂直领域。对于数据分析师、市场研究员以及AI算法工程师而言,小红书平台蕴含的价值是巨大的——无论是热门趋势洞察、KOL画像分析,还是多模态图文数据的采集。然而,想要高效、稳定地获取小红书App的公开数据,却并非易事。与Web端相比,小红书App的防护体系堪称“铜墙铁壁”。其核心的反爬机制建立在动态混淆的加密参数之上,尤其是请求头中的X-SX-T以及X-S-Common。

2201_76125261的博客 262

Java四种内存屏障详解,LoadLoad、LoadStore、StoreLoad、StoreStore

编译器和处理器可能会对指令进行重排以提高性能,但这种重排可能会导致其他线程看到不一致的状态。变量的读写操作前后会插入特定的内存屏障,这些屏障会禁止指令重排,从而保证了操作的顺序性。:当一条线程修改了一个变量的值,新值会立即被写入主内存,同时其他线程读取该变量时会从主内存中读取最新值,而不是使用线程缓存中的值。

化繁为简 1053

卷积码Viterbi译码算法原理及C语言实现

具体而言,卷积码的编码过程是将输入数据和编码器中的状态进行异或操作,输出码字为各个状态对应的结果。Viterbi译码算法是一种最大似然译码算法,根据接收到的含有噪声的码字,寻找出最有可能的原始数据序列。(2) 递推计算:根据接收到的码字和当前状态,计算下一时刻各个状态的路径度量,并记录每个状态的前驱状态。以上代码实现了一个简单的卷积码Viterbi译码算法,通过接收到的码字进行译码,并输出解码结果。(1) 初始化:将所有状态的路径度量初始化为一个很大的值,选择路径度量最小的状态作为起始状态。

2301_79331421的博客 825

Linux 4.19、Linux 5.10、Linux 6.1 三个版本详细分析

Linux 4.19发布日期:2018年10月22日LTS 状态:是长期支持版本,官方支持到2024年12月。Linux 5.10发布日期:2020年12月13日LTS 状态:LTS 版本,官方支持到2026年12月。Linux 6.1发布日期:2022年12月11日LTS 状态:不是官方的LTS版本,但通常社区会为新的版本提供一定时间的支持。定位:稳定性和长期支持,广泛应用于多种平台,尤其是嵌入式系统和服务器。主要优势:稳定性高,广泛的硬件支持,核心功能可靠。

小灰灰的博客 5951

JVM内存屏障(Memory Barrier)

# 内存屏障(Memory Barrier) 内存屏障可以禁止特定类型处理器的重排序,从而让程序按我们预想的流程去执行。内存屏障,又称内存栅栏,是一个CPU指令,基本上它是一条这样的指令: 保证特定操作的执行顺序。 影响某些数据(或则是某条指令的执行结果)的内存可见性。 编译器和CPU能够重排序指令,保证最终相同的结果,尝试优化性能。插入一条Memory Barrier会告诉编译器...

luoyoub的博客 5258

Windows窗口编程中的WM_PAINT、WM_ERASEBKGND、InvalidateRect、UpdateWindow、BeginPaint和EndPaint等函数详解

Windows窗口编程中的WM_PAINT、WM_ERASEBKGND、InvalidateRect、UpdateWindow、BeginPaint和EndPaint等函数详解

dvlinker的技术专栏 7165

Java内存模型Cookbook-前言

Java内存模型Cookbook-前言 原文地址:http://gee.cs.oswego.edu/dl/jmm/cookbook.html 作者:Doug Lea。由 JMM邮件组的成员提供帮助. dl@cs.oswego.edu. 译者:潘曦,丁一,古圣昌,欧振聪,方腾飞  校对:欧振聪,方腾飞 前言:从最初的探索至今已经有十年了。在此期间,很多关于处理器和语言的内存模型的规范和问题变...

weijiasihai的专栏 172

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

java cookbook_Java内存模型Cookbook(四)指南(Recipes)

作者:Doug Lea 翻译:丁一单处理器(Uniprocessors)如果能保证正在生成的代码只会运行在单个处理器上,那就可以跳过本节的其余部分。因为单处理器保持着明显的顺序一致性,除非对象内存以某种方式与可异步访问的IO内存共享,否则永远都不需要插入屏障指令。采用了特殊映射的java.nio buffers可能会出现这种情况,但也许只会影响内部的JVM支持代码,而不会影响Java代码。而且,可...

weixin_30989967的博客 214

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

内存屏障及其在 JVM 内的应用(下)

文末长摁二维码即可查看视频版本~Java Memory Model (JMM)Java 为了能在不同架构的 CPU 上运行,提炼出一套自己的内存模型,定义出来 Java 程序该怎么样和这...

LeanCloud通讯 448

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Java内存模型Cookbook

原文地址:http://gee.cs.oswego.edu/dl/jmm/cookbook.html 作者:Doug Lea。由 JMM邮件组的成员提供帮助. dl@cs.oswego.edu. 译者:潘曦,丁一,古圣昌,欧振聪,方腾飞  校对:欧振聪,方腾飞 前言:从最初的探索至今已经有十年了。在此期间,很多关于处理器和语言的内存模型的规范和问题变得更清楚,更容易理解,但还有一

414

java cookbook 3_Java内存模型Cookbook(三)多处理器

作者:Doug Lea 翻译:古圣昌 校对:欧振聪,方腾飞本文总结了在多处理器(MPs)中常用的的处理器列表,处理器相关的信息都可以从链接指向的文档中得到(一些网站需要通过注册才能得到相应的手册)。当然,这不是一个完全详细的列表,但已经包括了我所知道的在当前或者将来Java实现中所使用的多核处理器。下面所述的关于处理器的列表和内容也不一定权威。我只是总结一下我所阅读过的文档,但是这些文档也有可...

weixin_39593427的博客 130

JSR-133/Java内存模型(JMM)规范

JSR-133是Java内存模型(Java Memory Model,简称JMM)的规范,旨在解决Java并发编程中的内存可见性和指令重排序等问题。JSR-133主要规定了Java程序中多线程访问共享变量时的行为,以及编译器和JIT编译器对Java程序的优化方式。 具体来说,JSR-133主要做了以下几件事情:1. 确定了Java内存模型的规范,包括内存可见性、原子性、顺序性等方面。2. 规定了volatile变量的语义和使用方法,使得它们可以保证内存可见性。

于琦海 1459

Java内存模型Cookbook(四)指南(Recipes)

Java内存模型Cookbook(四)指南(Recipes) 原文:http://gee.cs.oswego.edu/dl/jmm/cookbook.html 作者:Doug Lea 翻译:丁一 前言 指令重排 内存屏障 多处理器 指南 单处理器(Uniprocessors) 如果能保证正在生成的代码只会运行在单个处理器上,那就可以跳过本节的其余部分。因为单处理器保持着明显的顺序一...

weijiasihai的专栏 164

Java内存模型Cookbook(三)多处理器

Java内存模型Cookbook(三)多处理器 原文:http://gee.cs.oswego.edu/dl/jmm/cookbook.html 作者:Doug Lea 翻译:古圣昌   校对:欧振聪,方腾飞 指令重排 内存屏障 多处理器 指南 本文总结了在多处理器(MPs)中常用的的处理器列表,处理器相关的信息都可以从链接指向的文档中得到(一些网站需要通过注册才能得到相应的手册)。当...

weijiasihai的专栏 215

JSR-133 Cookbook 要点摘录

文章目录01 重排Volatiles and MonitorsFinal Fields02 内存屏障分类 01 重排 对于编译器编写人员,JMM主要由禁止某些指令重排的一些规则构成。这些指令通常访问字段(包括数组元素)和监视器(锁)。 Volatiles and Monitors 在表中指示的第1个和第2个操作之间可能存在任意数量的其他操作。 Final Fields final字段的加载和存储作为与锁和volatile相关的“正常”访问,但是强加了两个额外的重新排序规则: 不能重新排序 x.fina

BruteForceMan的博客 231
上一篇: JMM Cookbook(一)指令重排
下一篇: 聊聊高并发(三十五)Java内存模型那些事(三)理解内存屏障
还不够
博客等级 码龄16年 113粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值