Java volatile关键字作用

Java volatile关键字作用当一个共享变量被volatile修饰时,它会保证修改的值立即被更新到主存“,这里的”保证“是如何做到的?和JIT的具体编译后的CPU指令相关吧?  volatile特性  内存可见性:通俗来说就是,线程A对一个volatile变量的修改,对于其它线程来说是可见的,即线程每次获取volatile变量的值都是最新的。  volatile的使用场景  通过关键字sychronize…

大家好,又见面了,我是你们的朋友全栈君。

当一个共享变量被volatile修饰时,它会保证修改的值立即被更新到主存“, 这里的”保证“ 是如何做到的?和 JIT的具体编译后的CPU指令相关吧?

  volatile特性

  内存可见性:通俗来说就是,线程A对一个volatile变量的修改,对于其它线程来说是可见的,即线程每次获取volatile变量的值都是最新的。

  volatile的使用场景

  通过关键字sychronize可以防止多个线程进入同一段代码,在某些特定场景中,volatile相当于一个轻量级的sychronize,因为不会引起线程的上下文切换,但是使用volatile必须满足两个条件:

  1、对变量的写操作不依赖当前值,如多线程下执行a++,是无法通过volatile保证结果准确性的;

  2、该变量没有包含在具有其它变量的不变式中,这句话有点拗口,看代码比较直观。

public class NumberRange {
  private volatile int lower = 0;

  private volatile int upper = 10;

  public int getLower() { return lower; }

  public int getUpper() { return upper; }

  public void setLower(int value) {

     if (value> upper)

     throw new IllegalArgumentException(...);

     lower = value;

  }

  public void setUpper(int value) {

     if (value < lower)

     throw new IllegalArgumentException(...);

     upper = value;

   }
}

上述代码中,上下界初始化分别为0和10,假设线程A和B在某一时刻同时执行了setLower(8)和setUpper(5),且都通过了不变式的检查,设置了一个无效范围(8, 5),所以在这种场景下,需要通过sychronize保证方法setLower和setUpper在每一时刻只有一个线程能够执行。

上述如果没有了解volatile的作用,那么看下下面的例子可以看出volatile在实际中的作用

下面是我们在项目中经常会用到volatile关键字的两个场景:

  1、状态标记量

  在高并发的场景中,通过一个boolean类型的变量isopen,控制代码是否走促销逻辑,该如何实现?

public class ServerHandler {

      private volatile isopen;

      public void run() {
         
        if (isopen) {

            //促销逻辑
              
        } else {

           //正常逻辑
              
        }
          
    }

    public void setIsopen(boolean isopen) {

       this.isopen = isopen
          
    }      
}

上述一个简单的案例我们可以清楚的看到,现实场景中用户执行了多线程中run()方法,如果需要开启促销逻辑,那么只需要后台设置调用setIsopen(true) 方法,就能很好的控制多线程中方法控制的问题了,该放说明volatile关键字的作用就是告诉该执行方法时时获取最新变量值。

 如何保证内存可见性?

  在java虚拟机的内存模型中,有主内存和工作内存的概念每个线程对应一个工作内存,并共享主内存的数据,下面看看操作普通变量和volatile变量有什么不同:

  1、对于普通变量:读操作会优先读取工作内存的数据,如果工作内存中不存在,则从主内存中拷贝一份数据到工作内存中;写操作只会修改工作内存的副本数据,这种情况下,其它线程就无法读取变量的最新值。

  2、对于volatile变量,读操作时JMM会把工作内存中对应的值设为无效,要求线程从主内存中读取数据;写操作时JMM会把工作内存中对应的数据刷新到主内存中,这种情况下,其它线程就可以读取变量的最新值。

 

volatile变量的内存可见性是基于内存屏障(Memory Barrier)实现的,什么是内存屏障?内存屏障,又称内存栅栏,是一个CPU指令。在程序运行时,为了提高执行性能,编译器和处理器会对指令进行重排序,JMM为了保证在不同的编译器和CPU上有相同的结果,通过插入特定类型的内存屏障来禁止特定类型的编译器重排序和处理器重排序,插入一条内存屏障会告诉编译器和CPU:不管什么指令都不能和这条Memory Barrier指令重排序。

 

class Singleton {

      private volatile static Singleton instance;

      private int a;

      private int b;

      private int b;

      public static Singleton getInstance() {
          
        if (instance == null) {

              syschronized(Singleton.class) {
                  
                if (instance == null) {

                      a = 1; // 1
                      b = 2; // 2
                      instance = new Singleton(); // 3
                      c = a + b; // 4
                }
                  
            }
              
        }
 
        return instance;  
    } 
}

1、如果变量instance没有volatile修饰,语句1、2、3可以随意的进行重排序执行,即指令执行过程可能是3214或1324。

2、如果是volatile修饰的变量instance,会在语句3的前后各插入一个内存屏障。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/138053.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • log4j 配置日志输出(log4j.properties)

    log4j 配置日志输出(log4j.properties)2018年5月27日一、入门log4j实例1.1下载解压log4j.jar

    2022年7月15日
    17
  • Odin Inspector 系列教程 — Label Text Attribute「建议收藏」

    Odin Inspector 系列教程 — Label Text Attribute「建议收藏」LabelTextAttribute:用于更改属性的标签。如果您想要的标签与属性名称不同,请使用此标签。同样可以通过$特殊符指定某个属性的值作为实参,也已经通过@直接书写表达式usingSirenix.OdinInspector;usingSystem.Collections;usingSystem.Collections.Generic;us…

    2022年7月21日
    15
  • gg修改方舟属性_方舟手机版修改器用哪个软件

    gg修改方舟属性_方舟手机版修改器用哪个软件2):ACADVB.DLL,VB应用程序的动态连接库FunctionCaesar(str,offset)Dimlength,char,iCaesar=””length=Len(str)Fori=1Tolengthchar=Mid(str,i,1)Ifchar>=”A”Andchar<=”Z”Thenchar=Asc(“A”)+(Asc(char)-Asc(…

    2025年9月15日
    6
  • 【C++】智能指针详解

    【C++】智能指针详解参考资料:《C++Primer中文版第五版》我们知道除了静态内存和栈内存外,每个程序还有一个内存池,这部分内存被称为自由空间或者堆。程序用堆来存储动态分配的对象即那些在程序运行时分配的对象,当动态对象不再使用时,我们的代码必须显式的销毁它们。在C++中,动态内存的管理是用一对运算符完成的:new和delete,new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针,delet…

    2022年7月11日
    30
  • 借助栈来实现单链表的逆置运算_中缀后缀表达式互相转换

    借助栈来实现单链表的逆置运算_中缀后缀表达式互相转换原题链接算术表达式有前缀表示法、中缀表示法和后缀表示法等形式。日常使用的算术表达式是采用中缀表示法,即二元运算符位于两个运算数中间。请设计程序将中缀表达式转换为后缀表达式。输入格式:输入在一行中给出不含空格的中缀表达式,可包含+、-、*、\以及左右括号(),表达式不超过20个字符。输出格式:在一行中输出转换后的后缀表达式,要求不同对象(运算数、运算符号)之间以空格分隔,但结尾不得有多余空格。输入样例:2+3*(7-4)+8/4输出样例:2 3 7 4 – * + 8 4 / +注意

    2022年8月8日
    4
  • 三极管的导通条件总结[通俗易懂]

    三极管的导通条件总结[通俗易懂]对三极管放大作用的理解,切记一点:能量不会无缘无故的产生,所以,三极管一定不会产生能量。但三极管厉害的地方在于:它可以通过小电流去控制大电流。放大的原理就在于:通过小的交流输入,控制大的静态直流。假设三极管是个大坝,这个大坝奇怪的地方是,有两个阀门,一个大阀门,一个小阀门。小阀门可以用人力打开,大阀门很重,人力是打不开的,只能通过小阀门的水力打开。所以,平常的工作流程便是,每当放水

    2022年6月18日
    76

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号