7_线程安全_线程间的内存可视性2缓存_内存屏障_读写排序

接着上次的问题------并不意味着最终的结果是"2"。

为什么?因为涉及到高速缓存与内存总线的工作细节。处理器可能有高速缓存,用来保存最近从主存中读出数据的拷贝的快速本地内存。在一个"回写"(write-back)高速缓存系统中,数据最初只是保存在高速缓存中,而在后面的时间写入(刷新)主存。在不保证写顺序的系统中,高速缓存中的数据块可能在任何处理器认为方便的时候写入主存。如果两个处理器向同一内存地址写不同的数据,则不同的数据将分别保存在二者的高速缓存中。最终两个值将写到主存中,但是是在随机的时刻写入,与写到相应的高速缓存中的顺序无关。

即使同一线程(或处理器)中的两个写操作也不需要在内存中表现相同的顺序。内存控制器可能发现以相反的顺序写入会更快或更方便。

问题还不仅局限于两个线程写内存。想像一下,一个线程在某个处理器上向内存中写入数据,而另一个线程在其他处理器上读相同的内存。好像很显然地,线程将得到最近写入的值,而在有些硬件上的确如此。这种现象有时被称为"内存一致性"或"读写排序"。但是在处理器之间确保这种同步是复杂的,它会降低内存系统的速度,并且对大部分代码没有任何意义。现在很多的计算机(通常是最快的计算机)不再保证不同处理器间内存访问的顺序,除非程序使用特殊的指令,这些指令就是常说的"内存屏障"(memory barrier)。

内存屏障确保:所有在内存屏障之前发起的内存访问,必须先于在设置屏障之后发起的内存访问之前完成。

内存屏障是一堵移动的墙,而不是刷新cache的命令。

即使没有读写排序和内存屏障,写内存好像也必须是原子性的操作。即任何其他线程只能看到完整的旧数据或者看到完整的新数据。但这也并不总是正确的。大部分计算机有一个天然的内存粒度,依赖于内存的组织和总线结构。即使处理器读写8位数据,内存传输可能以32位或64位为单位。这意味着,相对于其他覆盖相同32或64位数据单元的内存操作而言,8位写操作不是原子操作。大部分计算机会写包含修改数据的整个内存数据单元(如32位)。如果两个线程向同一个32位内存单元中写入不同的8位数据,则结果是最后写入的线程将确定两个8位字节的值,即覆盖第一个线程写入的值。图3.8显示了这种效果。

聪明的你是不是想到了一种情况------如果一个变量跨越了内存单元的界限(这将在允许对齐内存访问的机器上出现),计算机不得不在两次总线事物中传送数据。例如,一个非对齐的32位数据可能需要写两个相邻的32位内存单元。如果其中一个内存单元同时被另一个处理器写入,则将丢失一半数据。这被称为"word tearing",如图3.9所示。

欢迎关注,持续前进。

相关推荐
豆奶特浓61 小时前
谢飞机勇闯Java面试:从内容社区的缓存一致性到AI Agent,这次能飞多高?
java·微服务·ai·面试·架构·缓存一致性·feed流
CV_J1 小时前
L12_用户菜单权限
java
来旺1 小时前
互联网大厂Java面试实战:核心技术栈与业务场景深度解析
java·spring boot·docker·kubernetes·mybatis·hibernate·microservices
big-seal1 小时前
XML解释
xml·java·数据库
安卓理事人1 小时前
安卓临时缓存sp工具类
android·缓存
m***11901 小时前
Spring BOOT 启动参数
java·spring boot·后端
m***66731 小时前
Python 爬虫实战案例 - 获取社交平台事件热度并进行影响分析
开发语言·爬虫·python
小石头 100861 小时前
【JavaEE】synchronized关键字
java·java-ee
悟空码字1 小时前
Java实现接口幂等性:程序员的“后悔药”
java·后端