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所示。

欢迎关注,持续前进。

相关推荐
怒放吧德德21 小时前
Netty 4.2 入门指南:从概念到第一个程序
java·后端·netty
雨中飘荡的记忆1 天前
大流量下库存扣减的数据库瓶颈:Redis分片缓存解决方案
java·redis·后端
心之语歌1 天前
基于注解+拦截器的API动态路由实现方案
java·后端
华仔啊1 天前
Stream 代码越写越难看?JDFrame 让 Java 逻辑回归优雅
java·后端
ray_liang1 天前
用六边形架构与整洁架构对比是伪命题?
java·架构
Ray Liang1 天前
用六边形架构与整洁架构对比是伪命题?
java·python·c#·架构设计
Java水解1 天前
Java 中间件:Dubbo 服务降级(Mock 机制)
java·后端
SimonKing1 天前
OpenCode AI辅助编程,不一样的编程思路,不写一行代码
java·后端·程序员
FastBean1 天前
Jackson View Extension Spring Boot Starter
java·后端
Seven971 天前
剑指offer-79、最⻓不含重复字符的⼦字符串
java