精华内容
下载资源
问答
  • volatilemesi(缓存一致性)

    千次阅读 2020-06-03 11:45:06
    首先强调一点,volatilemesi这两个东西没有半点关系。mesi是缓存一致性的一种实现手段,多核CPU为了保证缓存数据的一致性,通常有两种实现手段,一种是总线锁,另一种是缓存锁。总线锁性能消耗大,缓存锁则一般...

    首先强调一点,volatile和mesi这两个东西没有半点关系。mesi是缓存一致性的一种实现手段,多核CPU为了保证缓存数据的一致性,通常有两种实现手段,一种是总线锁,另一种是缓存锁。总线锁性能消耗大,缓存锁则一般通过缓存一致性来实现。因此我们知道mesi是CPU硬件级别的。 volatile是JAVA的一种关键字,实现了两个功能: 1.可见性 2.禁止乱序。 禁止乱序,在JVM层面使用内存屏障来实现,汇编级别通过lock #指令来实现。

    问题:既然CPU有了MESI协议可以保证cache的一致性,那么为什么还需要volatile这个关键词来保证可见性(内存屏障)?或者是只有加了volatile的变量在多核cpu执行的时候才会触发缓存一致性协议?

     

    两个解释结论:

    多核情况下,所有的cpu操作都会涉及缓存一致性的校验,只不过该协议是弱一致性,不能保证一个线程修改变量后,其他线程立马可见,也就是说虽然其他CPU状态已经置为无效,但是当前CPU可能将数据修改之后又去做其他事情,没有来得及将修改后的变量刷新回主存,而如果此时其他CPU需要使用该变量,则又会从主存中读取到旧的值。而volatile则可以保证可见性,即立即刷新回主存,修改操作和写回操作必须是一个原子操作;
    正常情况下,系统操作并不会进行缓存一致性的校验,只有变量被volatile修饰了,该变量所在的缓存行才被赋予缓存一致性的校验功能。

    这个解释仅供参考。。。。。。。。。。。。。。。。。

     

    我们再来看一下另一位大N的解释:

    首先,volatile是java语言层面给出的保证,MSEI协议是多核cpu保证cache一致性(后面会细说这个一致性)的一种方法,中间隔的还很远,我们可以先来做几个假设:

    1. 回到远古时候,那个时候cpu只有单核,或者是多核但是保证sequence consistency[1],当然也无所谓有没有MESI协议了。那这个时候,我们需要java语言层面的volatile的支持吗?当然是需要的,因为在语言层面编译器和虚拟机为了做性能优化,可能会存在指令重排的可能,而volatile给我们提供了一种能力,我们可以告诉编译器,什么可以重排,什么不可以。
    2. 那好,假设更进一步,假设java语言层面不会对指令做任何的优化重排,那在多核cpu的场景下,我们还需要volatile关键字吗?答案仍然是需要的。因为 MESI只是保证了多核cpu的独占cache之间的一致性,但是cpu的并不是直接把数据写入L1 cache的,中间还可能有store buffer。有些arm和power架构的cpu还可能有load buffer或者invalid queue等等。因此,有MESI协议远远不够。
    3. 再接着,让我们再做一个更大胆的假设。假设cpu中这类store buffer/invalid queue等等都不存在了,cpu是数据是直接写入cache的,读取也是直接从cache读的,那还需要volatile关键字吗?你猜的没错,还需要的。原因就在这个“一致性”上。consistency和coherence都可以被翻译为一致性,但是MSEI协议这里保证的仅仅coherence而不是consistency。那consistency和cohence有什么区别呢?下面取自wiki[2]的一段话:
    Coherence deals with maintaining a global order in which writes to a single location or single variable are seen by all processors. Consistency deals with the ordering of operations to multiple locations with respect to all processors.

    因此,MESI协议最多只是保证了对于一个变量,在多个核上的读写顺序,对于多个变量而言是没有任何保证的。很遗憾,还是需要volatile~~

    4. 好的,到了现在这步,我们再来做最后一个假设,假设cpu写cache都是按照指令顺序fifo写的,那现在可以抛弃volatile了吧?你觉得呢?我都写到标题4了,那肯定不行啊!因为对于arm和power这个weak consistency[3]的架构的cpu来说,它们只会保证指令之间有比如控制依赖,数据依赖,地址依赖等等依赖关系的指令间提交的先后顺序,而对于完全没有依赖关系的指令,比如x=1;y=2,它们是不会保证执行提交的顺序的,除非你使用了volatile,java把volatile编译成arm和power能够识别的barrier指令,这个时候才是按顺序的。

    最后总结上文,答案就是:还需要~~

    引用:https://www.zhihu.com/question/296949412/answer/747494794

    展开全文
  • volatileMESI缓存一致性协议volatile实例多级缓存结构总线锁多核CPU多级缓存一致性协议MESIMESI协议缓存状态MESI状态转换MESI状态切换举例MESI优化和引入的问题CPU切换状态阻塞解决­存储缓存(Store Bufferes)...

    volatile

    volatile是Java虚拟机提供的轻量级的同步机制。

    • 不能保证原子性
    • 保证volatile修饰的共享变量对所有线程是可见的。也就是当一个线程修改 了一个被volatile修饰共享变量的值,新值总是可以被其他线程立即得知。
    • 禁止指令重排序

    关于volatile的相关知识点,已经在以下两篇博文中详细解释了。
    volatile详解1
    volatile详解2

    实例

    在这里插入图片描述
    volatile为什么会保证有序性?而不能保证原子性?
    在对volatile属性进行操作的代码转换为汇编语言语言时,汇编指令代码有lock指令;
    查看关于Lock资料:

    • LOCK(前缀) 代表的是总线锁,这个指令对应用程序有用且允许被应用程序使用。
    • 其在修改内存操作时,使用 LOCK 前缀去调用加锁的读-修改-写操作(原子的)。这种
      机制用于多处理器系统中处理器之间进行可靠的通讯。
    • 具体描述如下:
      在 Pentium 和早期的 IA-32 处理器中,LOCK 前缀会使处理器执行当前指令时产生
      一个 LOCK#信号,这总是引起显式总线锁定出现

    多级缓存结构

    在这里插入图片描述
    CPU厂商在CPU中内置了少量的高速缓存以解决I\O速度和CPU运算速度之间的不匹配问题

    总线锁

    在早期的读写操作中,多个CPU中执行的多个线程会经过BUS总线去内存内存中读数据,第一个线程是修改操作,第二个是读操作,就会发生数据的读写问题;
    早期的设计中,写操作的线程去内存中读取变量时,会在BUS总线上加上一个总线锁,其他CPU写操作且需要该变量的线程就无法从内存中获取该变量,全部都阻塞住了。因此,CPU的执行效率不高
    类比Java synchronized关键字只允许一个线程执行一段代码;
    在这里插入图片描述
    在这里插入图片描述

    多核CPU多级缓存一致性协议MESI

    多核CPU的情况下有多个一级缓存,如何保证缓存内部数据的一致,不让系统数据混乱。这里就引出了一个 一致性的协议MESI。

    MESI协议缓存状态

    即数据在缓存中的状态,一共有四种;

    • M 修改 (Modified):该Cache line有效,数据被修改了,和内存中的数据不一致,数据只存在于本Cache中。
    • E 独享、互斥 (Exclusive): 该Cache line有效,数据和内存中的数据一致,数据只存在于本Cache中。
    • S 共享 (Shared): 该Cache line有效,数据和内存中的数据一致,数据存在于很多Cache中
    • I 无效 (Invalid):该Cache line无效。

    注意:

    • 对于M和E状态而言总是精确的,他们在和该缓存行的真正状态是一致的,而S状态可能是非一致的。
    • 如果一个缓存将处于S状态的缓存行作废了,而另一个缓存实际上可能已经独享了该缓存行,但是该缓存却不会将该缓存行升迁为E状态,这是因为其它缓存不会广播他们作废掉该缓存行的通知,同样由于缓存并没有保存该缓存行的copy的数量,因此(即使有这种通知)也没有办法确定自己是否已经独享了该缓存行。 从上面的意义看来E状态是一种投机性的优化:如果一个CPU想修改一个处于S状态的缓存行,总线事务需 要将所有该缓存行的copy变成invalid状态,而修改E状态的缓存不需要使用总线事务

    MESI状态转换

    在这里插入图片描述
    触发事件描述

    本地读取(Local read)本地cache读取本地cache数据本地写入(Local write)
    本地写入(Local write)本地cache写入本地cache数据
    远端读取(Remote read)其他cache读取本地cache数据
    远端写入(Remote write)其他cache写入本地cache数据

    MESI状态切换举例

    假设有三个CPU A、B、C,对应三个缓存分别是cache a、b、 c。在主内存中定义了x的引用值为0。
    在这里插入图片描述

    1. 单核读取
      CPUA发出了一条指令,从主内存中读取X;
      从主内存通过BUS读取到缓存中(远端读取Remote READ),这是修改Cache Line的状态为E状态(独享);

    在这里插入图片描述
    2. 双核读取的执行流程:

    • CPUA发出了一条指令,从主内存中读取X;
    • 从主内存通过BUS读取到缓存中(远端读取Remote READ),这是修改Cache Line的状态为E状态(独享);
    • CPU B发出了一条指令,从主内存中读取x。
    • CPU B试图从主内存中读取x时,CPU A检测到了地址冲突。这时CPU A对相关数据做出响应。此时x 存储于cache a和cache b中,x在chche a和cache b中都被设置为S状态(共享)。
      在这里插入图片描述
    1. 修改数据的执行流程:
    • CPU A 计算完成后发指令需要修改x.
    • CPU A 将x设置为M状态(修改)
    • 并通知缓存了x的CPU B, CPU B将本地cache b中的x设置为I状态(无效),CPUB可能会把数据丢弃,也可能把数据放着,等到读取X的时候,将他给覆盖掉;
    • CPU A 对x进行赋值。
      在这里插入图片描述
    1. 同步数据的执行流程:
    • CPUB发出了读取X的指令
    • CPUB通知CPUA,CPUA将修改后的数据同步到主内存时,cache a 改为E;
    • CPU A同步CPUB的X ,将cache A ,和同步后cache b的x设置为S状态;
      在这里插入图片描述

    MESI优化和引入的问题

    缓存的一致性消息传递是需要的时间,这就使其切换时产生延迟。当一个缓存被切换状态时,其他缓存收到消息,完成各自的切换并且发出回应消息,这么一长串的时间中,CPU都会等待所有缓存响应完成。可能出现的阻塞都会导致各种各样的性能问题和稳定性问题;

    CPU切换状态阻塞解决­存储缓存(Store Bufferes)

    比如你需要修改本地缓存中的一条信息,那么你必须将I状态通知到其他拥有该缓存数据的CPU缓存中,并且等待确认。等待确认的过程会阻塞处理器,降低处理器性能;因为这个等待远远比一个指令的执行时间长的多;

    Store Bufferes

    为了避免CPU运算能力的浪费,Store Bufferes被引入使用,处理器把它想要写入到主内存的值写到缓存,然后继续处理其他事情。当所有失效确认收到时,数据才会被最终提交。
    这么做有两个风险:

    • 处理器会尝试从存储缓存(Store Bufferes)中读取值,但他还没有进行提交。这个的解决方案为为Store Forwarding,他使得加载的时候,如果存储缓存中存在,则进行返回。
    • 保存什么时候会完成,无法保证,可能会断电丢失。

    store buffer过程图

    在这里插入图片描述

    MESI失效

    当一个对象数据太大,超过了缓存行的容量64KB,无法锁住所有缓存行,会直接升级为总线锁,即该对象的修改操作,直接将总线锁住,其他线程需要修改该对象的都会被阻塞;

    展开全文
  • 目录前言二级目录三级目录 前言 我们先假设这样一个场景: 面试官:“小伙子,看你简历写了精通...你:“前面说了CPU缓存,在多核的情况下为了保证缓存一致性,CPU引入了MESI缓存一致性协议,” 二级目录 三级目录 ...

    1. 前言

    不知道从什么时候开始,百度volatile相关的文章基本上都是一个套路:

    先从CPU缓存架构、计算机的内存模型、MESI、volatile实时触发数据一致性……
    

    或许是从某个培训机构讲师嘴里说出来的,也可能是某位P9大佬呕心沥血花了几个月整理出来最后被共同封杀的笔记里记载的。

    一开始我也对这套理论坚信不疑,但最近看了一段相关的资料,发现这套理论一些细节并不能站稳脚跟,或者说讲得不够透彻。(PS:默默把自己之前发布的几篇相关文章删了)

    首先强调一点,volatile和MESI这两个东西没有必然关系。MESI是缓存一致性的一种实现手段,而volatile是在java层面只是jvm这款软件的一段代码增强,意图是保证变量的可见性和有序性


    2. volatile的作用与原理

    volatile在java层面只是JVM这款软件的一段代码增强,意图是保证变量的可见性和有序性。

    首先从可见性来说,虽然有缓存一致性协议可以保证各个CPU从缓存到主存之间的一致性。但问题是,数据得先到高速缓存才行啊,它可能还在写缓冲区storeBuffer。而且,对于有的CPU架构,还有无效化队列invalidQueue

    而volatile的目的就是告诉cpu,这个变量不需要缓冲区中而是每次都强制刷到缓存。只要刷到缓存,因为MESI或者其它缓存一致性协议的实现,各CPU缓存一致,所以即可实现可见性

    而有序性则是做了两件事情:

    1. 禁止编译器进行指令重排序
    2. 使用内存屏障来避免storeBuffer和invaildQueue造成的指令乱序


    3. 为什么有了MESI还需要volatile关键字

    其实理解了上面你就可以不用读这个了。

    明确一个点,缓存一致性协议是为了保证多个cache与内存之间的数据同步,但是volatile的语义是为了从软件层面来保证可见性和有序性。

    它并不关注底层是通过什么技术来实现的,因此单靠MESI并不能满足volatile,它只是volatile语义实现的一部分

    MESI也是默认生效的,要不然你以为操作系统线程间的数据可见性是怎么保证的,别再说什么volatile触发了MESI……


    4. 内存屏障是什么?它是怎么保证有序性的?

    内存屏障是干嘛的?防止指令重排序嘛,为什么会有指令重排序?指令重排是什么?怎么防止的?
    

    其实CPU真没有智能到能自己重排硬件指令,所有重排几乎都是storeBuffer和invaildQueue造成的,大部分CPU只是按照简单流水线方式去执行硬件指令

    不过编译器的指令重排确实是由volatile禁止了。

    4.1 Store Buffer & Invalid Queue

    MESI机制不止四个状态那么简单,除了四个状态之外,还有一套消息机制,正是因为这套消息机制带来了性能的下降, 于是也就有了 storeBuffer, invalidQueue等优化措施。

    但是这些优化措施又带来了另外的问题, 一些立马需要被其他CPU核心感知的修改, 因为storeBuffer的优化迟迟无法写入cache,MESI机制无法生效, 其他CPU核心就感知不到。

    所以CPU就提供了读、写屏障指令,让程序员或编译器明确声明,这里的修改需要立即写入cache, 不能在storeBuffer里存着,效果就是修改完变量后, 需要立即刷storeBuffer里的数据到cache,不能等CPU空闲时再刷。

    InvalidQueue差不多亦是如此。

    读、写屏障指令的本质就是把默认的异步刷buffer, 强制切换成同步刷buffer。

    5. synchronized是怎么保证可见性的?

    JMM中关于synchronized有如下规定,线程加锁时,必须清空工作内存中共享变量的值,从而使用共享变量时需要从主内存重新读取;线程在解锁时,需要把工作内存中最新的共享变量的值写入到主存,以此来保证共享变量的可见性。

    有序性?拜托,代码块都只允许一个线程访问了,它乱不乱序有什么影响吗?

    6. 相关资料

    这里留一些相关资料,本文大部分也是参考其中。
    我并没有去验证过这些理论的真实性,但至少这套理论是能够逻辑自洽的,并且深度超越了目前网络上大部分博文,看完之后希望大家能够提出自己的见解。

    1. JMM到底如何理解?JMM与MESI到底有没有关系?
    2. 既然CPU有缓存一致性协议(MESI),为什么JMM还需要volatile关键字?
    3. 当我们在谈论cpu指令乱序的时候,究竟在谈论什么?
    4. 缓存一致性协议的工作方式
    5. 内存屏障的来历
    展开全文
  • 说了很多,我们最后再回到最初的例子:volatile关键字实现了MESI协议,保证了多CPU(或多线程)对变量修改符合缓存一致性原则,加上volatile关键字后,确保了多线程对变量修改的可见性,在一处修改变量后,另一处...

    多线程下变量更新不同步问题

    测试一个通过修改while循环中标志位来结束一个线程的方法。使while循环中的内容为空,启动线程,线程死循环。当我们在主线程里把标志位flag置为false时,发现线程没有结束(即循环没有退出)。代码如下:

    public class Test {
            boolean flag=true;
    	    public static void main(String[] args){
    	    	Test test = new Test();
    	    	test.demo();
    	    }	
    	    public void demo(){
    	    	Thread t1 = new Thread(){	    		
    		    		public void run()  //重写run方法
    		    		{
    		    			while(flag==true)  
    		    			{			    		
    		    			}
    		    			System.out.println("线程结束了。。。。。。。。");
    		    		}		
    	    	};	    	
    	    	t1.start();
    	    	try {
    				Thread.sleep(10);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
                flag=false;
    	    }	
    }
    

    理论上在修改flag后线程会离开循环并输出结束语句,但是没有。当我在while循环中加上一些执行内容后,便可以正常退出。为什么会有这样的现象发生并且如何去解决这个问题呢?
    答案是:只要在变量的定义处加上volatile关键字就可以实现变量更新的同步。

    MESI协议和volatile关键字

    CPU的运算速度是远远大于CPU和主存之间的数据读写速度的,CPU中存在高速缓存(可以有多级),CPU可以和高速缓存之间进行高速的数据读写操作。比如要执行i=i+1这个语句,CPU会先向主存中读取i变量的数值,存入自己的高速缓存中,然后对i变量进行加一操作,写到高速缓存,最后再刷新到主存中。当对运算的要求比较高的时候,CPU会对高速缓存中的数据进行一系列操作后统一刷回主存,这就可以解释我们上面看到的现象了,为什么CPU中flag值没有被更新为false,在线程读取flag=true值到高速缓存中后,就在while循环中不断轮询这个变量值,因为我们这次没有加上打印语句(打印语句的执行时间是比较长的),为了保证读取的高效,CPU保持和其高速缓存进行交互,而没有重新去主存中读取新值(虽然主存中的值已经被修改了),循环就一直持续下去。
    CPU和主存交互示意图:
    在这里插入图片描述
    我们在变量的定义处加上了volatile关键字,解决了这个问题,线程可以被结束。如:volatile boolean flag=true;
    为什么呢?我们继续上面关于CPU和主存的讨论。多CPU单主存以及多线程并发访问主存会导致变量的不同步,这里我们称它为缓存一致性问题,只有解决了这个问题,才能实现在多线程并发访问的时候高效无误。
    解决缓存一致性问题的方法:
    一种方案是对总线加锁,使得每一时刻只能有一个CPU对总线进行访问,确保它对数据操作完之后再释放锁,允许别的CPU通过总线访问主存,这种方案实现了操作的原子性,但它会使得当一个CPU运作时别的CPU处于闲置状态,效率很低。现在广泛采用的方案是Intel的MESI协议,解决了缓存一致性问题并实现了高效。
    MESI协议:
    1、M(修改):当某CPU中的缓存条被修改后(和主存中的内容不一致),其他CPU中的相应缓存条会失效,如果有别的CPU需要访问该缓存条中的内容,则需要此修改后的缓存条把数据刷新到主存,然后把模式切换到共享(S)。
    2、E(独享):当某CPU中缓存条的内容和主存中的一致并且独享该缓存条的时候,有别的CPU需要访问该资源,那么只要把模式切换到共享(S)就可以了。
    3、S(共享):在这个模式下,任意CPU都可以访问此模式的缓存条中的数据,之后切换到相应模式就可以了。
    4、I(无效):此状态下的缓存条无效。
    MESI协议规定了缓存条的状态,从而实现了缓存一致性原则,在多CPU及多线程的访问下,保证了数据的同步。
    而volatile关键字就是对于这个协议的一种应用,加上它之后,在主线程中修改了flag的值,程序就会告诉另一个线程该位置缓存条已经失效,要求其从主存中重新读取flag值,从而退出了循环。
    volatile关键字实现了并发编程的两大要素,这里简单介绍一下:
    1、可见性:保证在多线程的情况下,某一变量对于多个线程来说都是“可见”的,这里的可见指的是当一个线程对变量修改之后,别的线程可以立刻知道,这实现了变量的同步。
    2、有序性:我们一般会认为程序的执行是逐上至下有序执行的,其实不然,由于java代码功能不同(处理的寄存器不同),为了提高效率,java会把具有相同功能的指令归类起来执行,减少不同类别指令之间的切换,从而提高流水线作业的效率,当然,这会保证程序执行的效果和顺序执行下的效果相同,但这仅限于单线程,在多线程中可能会出错,如以下伪代码:
    线程1:
    content=load();
    flag=true;
    线程2:
    while(flag==false)
    Thread.Sleep(100);
    do something with content…
    在线程1中,两条语句没有直接的逻辑关系,有可能交换顺序执行。但是到了线程2,如果在线程1未加载数据的情况下就把标志位置true引发线程2对数据的操作,就会出错。这时如果在flag的定义处加上volatile关键字,就可以避免重排。因为加了volatile关键字的变量就像是一个屏障,它下面的指令不能到它上面,它上面的指令也不能到它的下面,保证了有序性。
    **注:**并发编程另外一大重要的特性是原子性,volatile关键字不能保证。
    原子性:每个线程对该变量的操作都是原子的,原子操作是指,某个操作要不不执行,要不就一口气执行完并且保证不会被打断。原子操作我们在低层一般通过synchronize关键字来实现,用锁的机制来确保在执行这段程序的时候不会被干扰,同时这也体现了可见性,因为这是原子操作,执行完别的线程就会知道。volatile在执行类似:i++这样的指令时,需要经历加载变量,修改变量,在主存中更新变量这几步,不是原子操作,中间是可能被打断的。如果把i++放在synchronize关键字修饰的方法体中(加锁),就可以把它变成原子性操作。
    说了很多,我们最后再回到最初的例子:volatile关键字实现了MESI协议,保证了多CPU(或多线程)对变量修改符合缓存一致性原则,加上volatile关键字后,确保了多线程对变量修改的可见性,在一处修改变量后,另一处就要重新对变量进行读取,也就实现了线程的退出。

    展开全文
  • volatile和Cache一致性协议之MESI

    万次阅读 热门讨论 2017-08-14 17:46:06
    volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在Java 5之后,volatile关键字才得以重获生机。  volatile关键字...
  • https://www.wowodx.com/xinxiyujisuankexue/7fcf3737d630412fa3717ccc39966196.html
  • 缓存一致性协议和 java 的 volatile 实现无任何关系 cache line的概念 缓存行对齐 伪共享 a. cache是以cache line为单位与内存映射的,cache只要命中了就是命中64字节,对应内存中连续的64个地址,这64个地址刚好...
  • MESI的概念此处不再累赘,有兴趣的可以搜索 store buffer 引入store buffer是为了将同步改为异步 引入store forwarding技术是为了让CPU可以直接从store buffer里加载数据 但是因此可能会发生乱序情况,譬如a在...
  • 结论:MESI与JMM无关,更与volatile无关,volatile只是实现JMM的一部分 JMM是一种虚的概念,目的是实现java跨平台。其中,volatile可以解决JMM中存在的一些问题,但不能解决所有问题,比如不能解决原则性。但是可以...
  • mesi协议是Intel为了缓存一致性 (内存,cpu高速缓存)两线程同时读取内存内的值,然后进行+1操作,但是最后只加了1 解决 1.加锁 它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也...
  • 前面我们从操作系统底层了解了现代计算机结构模型中的CPU指令结构、CPU缓存结构、CPU运行调度以及操作系统内存管理,并且学习了Java内存模型(JMM)和 volatile 关键字的一些特性。本篇来深入理解CPU缓存一致性协议(..
  • 2.CAS指令,确保了对同一个同一个内存地址操作的原子性,那么他应该也会遇到和上面可见性一样的问题,他是怎么解决的,是不是和volatile的底层原理类似?--->是的,也是利用了MESI 3.volatile还避免了指令重排...
  • 初识volatile Java语言规范第3版中对volatile的定义如下:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。 这个概念听起来有些抽象,我们先看...
  • volatile作用——可见性 大家都应该知道 volatile 的主要作用有两点: 1、保证变量的内存可见性 ;2、禁止指令重排序,今天我们重点来谈谈可见性。 首先,在理解 volatile 可见性前,先来看一看一个多线程访问...
  • volatile底层实现(CPU的缓存一致性协议MESI)

    千次阅读 热门讨论 2019-07-20 10:59:59
    CPU的缓存一致性协议MESI 在多核CPU中,内存中的数据会在多个核心中存在数据副本,某一个核心发生修改操作,就产生了数据不一致的问题,而一致性协议正是用于保证多个CPU cache之间缓存共享数据的一致性。 cache的写...
  • 既然CPU有缓存一致性协议(MESI),为什么JMM还需要volatile关键字? MESI缓存一致性协议在哪里以及如何实现? Intel® 64 and IA-32 Architectures Developer’s Manual: Vol. 3A
  • 文章目录一、缓存一致性的要求二、总线相关概念1、总线裁决概念2、总线锁定和缓存锁定3、总线窥探概念(Bus Snooping)三、一致性协议(Coherence protocol)1、MESI协议2、伪共享的问题3、解决方法 写在前面:本文...
  • 3. volatile应用场景3 4. 内存屏障(Memory Barrier)4 5. setjmp和longjmp4 1) 结果1(非优化编译:g++ -g -o x x.cpp -O0) 5 2) 结果2(优化编译:g++ -g -o x x.cpp -O2) 6 6. 不同CPU架构的一致性模型6 ...
  • volatile实现原理分析
  • 1. 未加volatile关键字,由于缓存不一致性导致线程间数据交互失败 public class Test { private static int INIT_VAL = 0; private static final Integer MAX_VAL = 5; public static void main(String[] args...
  • 本文和后续文章将着眼CPU的工作原理阐述伪共享的解决方法和volatile关键字的应用。
  • MESI协议虽然可以实现缓存的一致性,但是也会存在一些问题。 就是各个CPU缓存行的状态是通过消息传递来进行的。如果CPU0要对一个在缓存中共享的变量进行写入,首先需要发送一个失效的消息给到其他缓存了该数据的CPU...
  • volatile关键字的作用

    万次阅读 多人点赞 2019-06-09 23:46:30
    volatile关键字的作用
  • 1.MESI 概念 MESI(Modified Exclusive Shared Or Invalid)(也称为伊利诺斯协议,是因为该协议由伊利诺斯州立大学提出)是一种广泛使用的支持写回策略的缓存一致性协议。 MESI协议中的状态:CPU中每个缓存行...
  • 来谈谈JMM2.volatile和synchronized关键字2.1volatile是如何保证可见性的??2.2 volatile是如何保证有序性的呢??哪些情况不能重排序??三级目录 1.来谈谈JMM 我们都知道,为了提高CPU的运行效率,我们会在CPU和内存之间...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,762
精华内容 2,304
关键字:

mesivolatile