volatile 订阅
volatile是一个特征修饰符(type specifier).volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。 展开全文
volatile是一个特征修饰符(type specifier).volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
信息
中文名
易变型变量
属    性
类型修饰符
外文名
volatile
释    义
易变的
词    性
形容词
volatile作用
简单地说就是防止编译器对代码进行优化。比如如下程序:对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。如果键入volatile,则编译器会逐一地进行编译并产生相应的机器代码(产生四条代码)。
收起全文
精华内容
下载资源
问答
  • 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的...
  • 本文给大家介绍了单片机C语言中volatile的作用。
  • volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情。这篇文章主要介绍了Java中volatile关键字的作用与用法详解的相关资料,需要的朋友可以参考下
  • 在本篇文章里小编给大家整理了关于Java的Volatile知识点相关内容,有需要的朋友们可以跟着学习下。
  • 主要介绍了通过volatile验证线程之间的可见性,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 在本文里我们给大家分享的是关于java volatile关键字作用及使用场景的相关知识点内容,需要的朋友们学习下。
  • 主要介绍了C语言编程中volatile修饰符的作用,文章深入到内存优化方面进行解析,非常推荐!需要的朋友可以参考下
  • 主要给大家介绍了关于C++中volatile关键字及常见的误解的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  •  __asm__ __volatile__内嵌汇编用法简述 在阅读C/C++原码时经常会遇到内联汇编的情况,下面简要介绍下__asm__ __volatile__内嵌汇编用法。因为我们华清远见教学平台是ARM体系结构的,所以下面的示例都是用ARM汇编。...
  • 下面小编就为大家带来一篇java中volatile不能保证线程安全(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • C#中volatile与lock用法

    2021-01-01 10:00:08
    本文实例讲述了C#中volatile与lock用法,分享给大家供大家参考。具体分析如下: 一、C#中volatile volatile是C#中用于控制同步的关键字,其意义是针对程序中一些敏感数据,不允许多线程同时访问,保证数据在任何访问...
  • 主要介绍了C++中const、volatile、mutable使用方法小结,需要的朋友可以参考下
  • volatile 实例讲解

    2020-08-03 05:47:54
    volatile的本意是一般有两种说法--1.“暂态的”;2.“易变的”。这两种说法都有可行。但是究竟volatile是什么意思,现举例说明(以Keil-c与a51为例,例子来自Keil FQA),看完例子后你应该明白volatile的意思了,如果还...
  • 让你学会C语言编程中volatile变量的使用
  • 主要介绍了Linux C中多线程与volatile变量的相关资料,需要的朋友可以参考下
  • volatile与synchronized的区别,锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)
  • C++中修饰数据可变的关键字有三个:const、volatile和mutable。const比较好理解,表示其修饰的内容不可改变(至少编译期不可改变),而volatile和mutable恰好相反,指示数据总是可变的。mutable和volatile均可以和...
  • const和volatile放在一起的意义在于: (1)本程序段中不能对a作修改,任何修改都是非法的,或者至少是粗心,编译器应该报错,防止这种粗心;(2)另一个程序段则完全有可能修改,因此编译器最好不要做太激进的优化...
  •  而volatile关键字是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。  使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,...
  • 在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候可以万事大吉。  Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 ...
  • C/C++中的volatile关键字和const对应,用来修饰变量,用于告诉编译器该变量值是不稳定的,可能被更改。使用volatile注意事项: (1). 编译器会对带有volatile关键字的变量禁用优化(A volatile specifier is a hint to...
  • Volatile

    千次阅读 2019-05-06 12:30:16
    同synchronized相比(synchronized通常称为重量级锁),volatile更轻量级,相比使用synchronized所带来的庞大开销,倘若能恰当的合理的使用volatile,自然是美事一桩。  为了能比较清晰彻底的理解volatile,我们...

     

    内存可见性

      volatile是Java提供的一种轻量级的同步机制,在并发编程中,它也扮演着比较重要的角色。同synchronized相比(synchronized通常称为重量级锁),volatile更轻量级,相比使用synchronized所带来的庞大开销,倘若能恰当的合理的使用volatile,自然是美事一桩。

      为了能比较清晰彻底的理解volatile,我们一步一步来分析。首先来看看如下代码

    public class TestVolatile {
        boolean status = false;
    
        /**
         * 状态切换为true
         */
        public void changeStatus(){
            status = true;
        }
    
        /**
         * 若状态为true,则running。
         */
        public void run(){
            if(status){
                System.out.println("running....");
            }
        }
    }

     

    上面这个例子,在多线程环境里,假设线程A执行changeStatus()方法后,线程B运行run()方法,可以保证输出"running....."吗?

      答案是NO! 

      这个结论会让人有些疑惑,可以理解。因为倘若在单线程模型里,先运行changeStatus方法,再执行run方法,自然是可以正确输出"running...."的;但是在多线程模型中,是没法做这种保证的。因为对于共享变量status来说,线程A的修改,对于线程B来讲,是"不可见"的。也就是说,线程B此时可能无法观测到status已被修改为true。那么什么是可见性呢?

      所谓可见性,是指当一条线程修改了共享变量的值,新值对于其他线程来说是可以立即得知的。很显然,上述的例子中是没有办法做到内存可见性的。

     

    Java内存模型

      为什么出现这种情况呢,我们需要先了解一下JMM(java内存模型)

      java虚拟机有自己的内存模型(Java Memory Model,JMM),JMM可以屏蔽掉各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的内存访问效果。

      JMM决定一个线程对共享变量的写入何时对另一个线程可见,JMM定义了线程和主内存之间的抽象关系:共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存保存了被该线程使用到的主内存的副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。这三者之间的交互关系如下

     

    需要注意的是,JMM是个抽象的内存模型,所以所谓的本地内存,主内存都是抽象概念,并不一定就真实的对应cpu缓存和物理内存。当然如果是出于理解的目的,这样对应起来也无不可。

      大概了解了JMM的简单定义后,问题就很容易理解了,对于普通的共享变量来讲,比如我们上文中的status,线程A将其修改为true这个动作发生在线程A的本地内存中,此时还未同步到主内存中去;而线程B缓存了status的初始值false,此时可能没有观测到status的值被修改了,所以就导致了上述的问题。那么这种共享变量在多线程模型中的不可见性如何解决呢?比较粗暴的方式自然就是加锁,但是此处使用synchronized或者Lock这些方式太重量级了,有点炮打蚊子的意思。比较合理的方式其实就是volatile

      volatile具备两种特性,第一就是保证共享变量对所有线程的可见性。将一个共享变量声明为volatile后,会有以下效应:

        1.当写一个volatile变量时,JMM会把该线程对应的本地内存中的变量强制刷新到主内存中去;

        2.这个写会操作会导致其他线程中的缓存无效。

    上面的例子只需将status声明为volatile,即可保证在线程A将其修改为true时,线程B可以立刻得知

    volatile boolean status = false

    留意复合类操作

      但是需要注意的是,我们一直在拿volatile和synchronized做对比,仅仅是因为这两个关键字在某些内存语义上有共通之处,volatile并不能完全替代synchronized,它依然是个轻量级锁,在很多场景下,volatile并不能胜任。看下这个例子:

    package test;
    
    import java.util.concurrent.CountDownLatch;
    
    /**
     * Created by chengxiao on 2017/3/18.
     */
    public class Counter {
        public static volatile int num = 0;
        //使用CountDownLatch来等待计算线程执行完
        static CountDownLatch countDownLatch = new CountDownLatch(30);
        public static void main(String []args) throws InterruptedException {
            //开启30个线程进行累加操作
            for(int i=0;i<30;i++){
                new Thread(){
                    public void run(){
                        for(int j=0;j<10000;j++){
                            num++;//自加操作
                        }
                        countDownLatch.countDown();
                    }
                }.start();
            }
            //等待计算线程执行完
            countDownLatch.await();
            System.out.println(num);
        }
    }

    执行结果:  224291

     

    针对这个示例,一些同学可能会觉得疑惑,如果用volatile修饰的共享变量可以保证可见性,那么结果不应该是300000么?

    问题就出在num++这个操作上,因为num++不是个原子性的操作,而是个复合操作。我们可以简单讲这个操作理解为由这三步组成:

      1.读取

      2.加一

      3.赋值

      所以,在多线程环境下,有可能线程A将num读取到本地内存中,此时其他线程可能已经将num增大了很多,线程A依然对过期的num进行自加,重新写到主存中,最终导致了num的结果不合预期,而是小于30000

    解决num++操作的原子性问题

      针对num++这类复合类的操作,可以使用java并发包中的原子操作类原子操作类是通过循环CAS的方式来保证其原子性的

     

    /**
     * Created by chengxiao on 2017/3/18.
     */
    public class Counter {
      //使用原子操作类
        public static AtomicInteger num = new AtomicInteger(0);
        //使用CountDownLatch来等待计算线程执行完
        static CountDownLatch countDownLatch = new CountDownLatch(30);
        public static void main(String []args) throws InterruptedException {
            //开启30个线程进行累加操作
            for(int i=0;i<30;i++){
                new Thread(){
                    public void run(){
                        for(int j=0;j<10000;j++){
                            num.incrementAndGet();//原子性的num++,通过循环CAS方式
                        }
                        countDownLatch.countDown();
                    }
                }.start();
            }
            //等待计算线程执行完
            countDownLatch.await();
            System.out.println(num);
        }
    }

     

    执行结果

    300000

     

    禁止指令重排序

    volatile还有一个特性:禁止指令重排序优化。

    重排序是指编译器和处理器为了优化程序性能而对指令序列进行排序的一种手段。但是重排序也需要遵守一定规则:

      1.重排序操作不会对存在数据依赖关系的操作进行重排序。

        比如:a=1;b=a; 这个指令序列,由于第二个操作依赖于第一个操作,所以在编译时和处理器运行时这两个操作不会被重排序。

      2.重排序是为了优化性能,但是不管怎么重排序,单线程下程序的执行结果不能被改变

        比如:a=1;b=2;c=a+b这三个操作,第一步(a=1)和第二步(b=2)由于不存在数据依赖关系,所以可能会发生重排序,但是c=a+b这个操作是不会被重排序的,因为需要保证最终的结果一定是c=a+b=3。

      重排序在单线程模式下是一定会保证最终结果的正确性,但是在多线程环境下,问题就出来了,来开个例子,我们对第一个TestVolatile的例子稍稍改进,再增加个共享变量a

    public class TestVolatile {
        int a = 1;
        boolean status = false;
    
        /**
         * 状态切换为true
         */
        public void changeStatus(){
            a = 2;//1
            status = true;//2
        }
    
        /**
         * 若状态为true,则running。
         */
        public void run(){
            if(status){//3
                int b = a+1;//4
                System.out.println(b);
            }
        }
    }

     

     

    假设线程A执行changeStatus后,线程B执行run,我们能保证在4处,b一定等于3么?

      答案依然是无法保证!也有可能b仍然为2。上面我们提到过,为了提供程序并行度,编译器和处理器可能会对指令进行重排序,而上例中的1和2由于不存在数据依赖关系,则有可能会被重排序,先执行status=true再执行a=2。而此时线程B会顺利到达4处,而线程A中a=2这个操作还未被执行,所以b=a+1的结果也有可能依然等于2。

      使用volatile关键字修饰共享变量便可以禁止这种重排序。若用volatile修饰共享变量,在编译时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序

      volatile禁止指令重排序也有一些规则,简单列举一下:

      1.当第二个操作是voaltile写时,无论第一个操作是什么,都不能进行重排序

      2.当地一个操作是volatile读时,不管第二个操作是什么,都不能进行重排序

      3.当第一个操作是volatile写时,第二个操作是volatile读时,不能进行重排序

    总结:

      简单总结下,volatile是一种轻量级的同步机制,它主要有两个特性:一是保证共享变量对所有线程的可见性;二是禁止指令重排序优化。同时需要注意的是,volatile对于单个的共享变量的读/写具有原子性,但是像num++这种复合操作,volatile无法保证其原子性,当然文中也提出了解决方案,就是使用并发包中的原子操作类,通过循环CAS地方式来保证num++操作的原子性。关于原子操作类,会在后续的文章进行介绍。

     

    作者: dreamcatcher-cx

    出处: <http://www.cnblogs.com/chengxiao/>

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在页面明显位置给出原文链接

     

    展开全文
  • NULL 博文链接:https://kiral.iteye.com/blog/1416354
  • 为什么使用volatile ? C/C++中的 volatile 关键字 和const对应,用来修饰变量,通常用于建立语言级别的memory barrier。这是BS在“The C++ Programming Language”对volatile修饰词的解释: A volatile specifier ...
  •  __asm__ __volatile__内嵌汇编用法简述 在阅读C/C++原码时经常会遇到内联汇编的情况,下面简要介绍下__asm__ __volatile__内嵌汇编用法。因为我们华清远见教学平台是ARM体系结构的,所以下面的示例都是用ARM汇编。...
  •  volatile在词典中的意思是易变的,反复无常的。它在我们的程序设计中常常用到的。volatile是一个关键字,用来修饰一个变量,告诉编译器在编译的时候不要对其进行优化,在操作寄存器和多线程中这种用法是最常见的。...
  • 本文详细解读一下volatile关键字如何保证变量在多线程之间的可见性,对Java中volatile关键字实现原理感兴趣的朋友一起通过本文学习吧
  • 最近写的关于在嵌入式开发中常遇到的关于volatile关键字使用的短文,都是些通用的技术,贴上来share。
  • volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 330,411
精华内容 132,164
关键字:

volatile