java多线程基础之volatile

接下来几天,我会写写关于多线程方面的博客,今天的主题是volatile关键字。

  • 首先,先写一下Java内存模型的相关知识。

    • JMM一般都是围绕原子、可见、有序性三点讨论,在此仅是简单介绍。

    • 工作内存与主存定义:JVM定义了规定了所有变量都是存在主存里,每个线程都有自己的工作内存,线程对变量的所有操作都必须在其工作内存上进行,而不是直接对主存进行操作,而且每个线程都不能访问其它线程的工作内存。

    • 从图中,可知道高并发环境下,线程并不是直接去修改内存中的值,而是先去修改本身持有的副本。

      java多线程基础之volatile

  • 浅谈,volatile是什么?

    • 为什么要用volatile?原因是在JMM中为了在适当的场合,确保线程间的有序性和可见性,java使用了一些特殊操作或者关键字来申明告诉虚拟机这个地方需要特别注意,不能随便变动优化目标指令。关键字volatile便是其中之一。
    • 当使用voaltile去修改一个变量的时候,就等于告诉虚拟机,这个变量是不稳定的,极有可能被某段程序或其他线程修改,为了确保这个变量在被修改后,让该应用程序中的所有线程能及时"看见",那么虚拟机就需要采取一些特殊手段保证该变量的可见性。
    • 保证可见性的简略过程,类似MESI协议:
      1. 预设两个线程,名称分别为A和B。使用volatile关键字修饰的时候,线程B进行修改的时候,会导致线程A的工作内存中的副本变成无效状态。那么,线程A再读取变量的时候,发现其无效,则会重新去主内存中读取。
  • volatile内存可见性。

    • 保证volatile内存可见性,即当一个变量被volatile修饰的时候,该变量发生修改后会立即更新到主存中,其他线程再读取的时候,都会去主存中读取最新的值。
    • 要实现volatile内存可见性的语义,那么在加入volatile关键字,并且编译器在生成字节码的时候,会在指令序列中插入内存屏障保证指令按照顺序执行。
  • volatile的内存屏障

    • 内存屏障,是一组CPU指令,这个指令可以保证屏障前后的指令遵循一定的顺序,并且保证可见性。

    • 内存屏障的作用有两个:

      1. 其一,先于这个内存屏障的指令先执行,后于这个内存屏障的指令必须后执行 。
      2. 其二,使得内存可见性。使用读指令前插入读屏障,可以让Cache中的数据失效,重新从主内存中加载数据。在写指令之后插入写屏障,能让写入缓存的最新数据加载到主内存。
    • 简单介绍几个基本内存屏障指令

      1. StoreStore、StroreLoad 、LoadLoad 、LoadStore

      2. volatile写操作,内存屏障插入示意图:

        • StoreStore:在普通写与volatile写之间插入SS屏障,保证在volatile写及后续写入操作执行前,保证普通写入操作对其他处理器是可见的。即,禁止普通写与volatile写进行重排序。
        • StoreLoad:保证SL屏障后的所有读操作执行前,保证volatile写操作对其他处理器是可见的。即,防止volatile写与SS之后出现的读写操作进行重排序。

        java多线程基础之volatile

      3. volatile读取操作,内存屏障插入示意图:

        • LoadLoad:禁止下面的所有普通读操作和vilatile读重排序。
        • LoadStore屏障:禁止下面所有的写操作和volatile读重排序。

        java多线程基础之volatile