到底什么是内存可见性?

简介: 到底什么是内存可见性?

我们都知道,volatile保证了内存可见性和禁止指令重排,但是对于内存可见性这一条,我一直没有完全弄明白,今天咱们一起看一下,这个可见性,到底是如何可见,数据到底是如何可见的。


首先我们要达成一个共识:单核CPU由于同一时刻只会有一个线程执行,而每个线程执行的时候操作的都是同一个CPU的缓存,所以,单核CPU不存在可见性问题。


要了解清楚什么是内存可见性,我们需要先明确几个关键字的含义。


定义


cpu缓存



今天主流的CPU架构来说,现在的CPU主要采用三层缓存:


L1、L2缓存成为本地核心内缓存,即一个核一个。如果你的机器是4核,


那就是有4个L1+4个L2


L3缓存是所有核共享的。即不管你的CPU是几核,这个CPU中只有一个L3


L1缓存的大小是64K,即32K指令缓存+32K数据缓存。L2是256K,L3是2M。


MESI协议


MESI其实对应的是缓存中缓存行(CPU缓存存储数据的最小单位,大小为64B)的四种状态。


  1. 已修改Modified (M)


缓存行是脏的(dirty),与主存的值不同。如果别的CPU内核要读主存这块数据,该缓存行必须回写到主存,状态变为共享(S).


  1. 独占Exclusive (E)


缓存行只在当前缓存中,但是干净的(clean)–缓存数据同于主存数据。当别的缓存读取它时,状态变为共享;当前写数据时,变为已修改状态。


  1. 共享Shared (S)


缓存行也存在于其它缓存中且是干净的。缓存行可以在任意时刻抛弃。


  1. 无效Invalid (I)


缓存行是无效的


举例


明确了定义,接下来我们用一个具体示例来描述一下MESI的可见性是如何起效的。


i=0; i=i+1;


执行这条语句的时候,在某个核上运行的某线程将 i 的值拷贝一个副本到此核所在的缓存中,当运算执行完成后,再回写到主存中去。如果是多线程环境下,每一个线程都会在所运行的核上的高速缓存区有一个对应的工作内存,也就是每一个线程都有自己的私有工作缓存区,用来存放运算需要的副本数据。那么,我们再来看这个 i+1 的问题,假设 i 的初始值为0,有两个线程同时执行这条语句,有以下几个步骤:


  1. 程序以及数据被加载到主内存


  1. 指令和数据被加载到CPU的高速缓存


  1. CPU执行指令,把结果写到高速缓存


  1. 高速缓存中的数据写回主内存


我们先讨论volatile是如何保证可见性的,假设此时线程1被cpu0执行,线程2被cpu1执行,cpu0和cpu1同时把i=0这个值从内存读取到了自己的缓存中,那么,根据MESI协议,此时cpu0和cpu1中的该缓存状态都变成S(共享),然后此时cpu0计算完成i+1=1,cpu1也计算完成i+1=1,cpu0首先将结果写回了主存,此时,cpu1缓存中的i值,将会变为I(无效)状态,此后如果cpu1再去读取i,就要重新从主存拿值,可是此时cpu1其实已经计算完成,所以cpu1也将结果1写回了主存。


从这个例子看,虽然保证了可见性,可是最终的计算结果还是错误的。这是因为volatile没有保证原子性,线程执行的这个i=i+1不是一个完整的不可分割的计算过程。


参考


Volatile:内存屏障原理应该没有比这篇文章讲的更清楚了


MESI协议


Java 开发, volatile 你必须了解一下

目录
相关文章
|
2月前
|
存储 缓存 Java
【JavaEE】——内存可见性问题
volatile,一个线程读,一个线程写,两个线程互相读,多个线程多把锁
|
4月前
|
缓存 Java 编译器
【多线程-从零开始-伍】volatile关键字和内存可见性问题
【多线程-从零开始-伍】volatile关键字和内存可见性问题
75 0
|
7月前
|
安全 Java 开发者
探索Java内存模型:可见性、有序性和并发
在Java的并发编程领域中,内存模型扮演了至关重要的角色。本文旨在深入探讨Java内存模型的核心概念,包括可见性、有序性和它们对并发实践的影响。我们将通过具体示例和底层原理分析,揭示这些概念如何协同工作以确保跨线程操作的正确性,并指导开发者编写高效且线程安全的代码。
|
7月前
|
缓存 安全 Java
Java面试题:解释volatile关键字的作用,以及它如何保证内存的可见性
Java面试题:解释volatile关键字的作用,以及它如何保证内存的可见性
110 4
|
7月前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
107 1
|
7月前
|
安全 Java 开发者
Java面试题:Java内存模型解析,Java内存模型的基本概念和它的重要性,Java内存模型中的“可见性”和“有序性”,以及具体实现?
Java面试题:Java内存模型解析,Java内存模型的基本概念和它的重要性,Java内存模型中的“可见性”和“有序性”,以及具体实现?
81 1
|
8月前
|
缓存 Java 程序员
Java内存模型深度解析:可见性、有序性和原子性
在多线程编程中,正确理解Java内存模型对于编写高效且无bug的并行程序至关重要。本文将深入探讨JMM的三大核心特性:可见性、有序性和原子性,并结合实例分析如何利用这些特性来避免常见的并发问题。
81 1
|
7月前
|
存储 缓存 安全
Java面试题:介绍一下jvm中的内存模型?说明volatile关键字的作用,以及它如何保证可见性和有序性。
Java面试题:介绍一下jvm中的内存模型?说明volatile关键字的作用,以及它如何保证可见性和有序性。
49 0
|
7月前
|
Java 开发者
Java面试题:解释Java内存模型中的内存可见性,解释Java中的线程池(ThreadPool)的工作原理,解释Java中的CountDownLatch和CyclicBarrier的区别
Java面试题:解释Java内存模型中的内存可见性,解释Java中的线程池(ThreadPool)的工作原理,解释Java中的CountDownLatch和CyclicBarrier的区别
45 0
|
7月前
|
存储 安全 Java
Java面试题:Java内存模型中的主内存与工作内存是如何协同工作的?请解释Java内存模型中的可见性、原子性和有序性,举例说明Java内存模型中的happens-before关系
Java面试题:Java内存模型中的主内存与工作内存是如何协同工作的?请解释Java内存模型中的可见性、原子性和有序性,举例说明Java内存模型中的happens-before关系
59 0