`
scanfprintf123
  • 浏览: 80558 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Volatile语义的一些探讨

阅读更多

  好吧,这里我只想说说volatile在JMM中的语义。

 

  当我们在使用volatile的时候,实际上它表达了下面那么些意思。

 

   1. 可见性。

      这个是大多数人都知道的一个特质, JAVA的线程有自己的工作内存区,与主存区不同,当我们对变量使用了volatile后,那么不管对这个变量的读或写,都会在主存中进行,而不会在处理器的缓存或者寄存器中进行。这个很好理解。

 

 

   2. 禁止CPU指令的重排序

      这个特质的理解稍微要花点脑细胞, 首先我们需要一点premilinary, 当我们的程序编写好了以后,会被翻译成指令集并被加载到内存中去运行。但是,在CPU真正执行的时候,处于性能方面的考虑,这些指令的执行不一定会按照程序中的顺序进行,只要保证其程序执行语义没有变化即可。

 

     来看下代码,

class VolatileExample {
  int x = 0;
  volatile boolean v = false;

  //in thread A
  public void writer() {
    x = 42;
    v = true;
  }

  //in thread B
  public void reader() {
    if (v == true) {
      //uses x - can we see x is 42?
    }
  }
}
 

    在这种情况下,当thread A 执行完后,在thread B中能看到x等于42吗?(变量v肯定可以看到,根据可见性可以推断出来) 好吧,在旧的JMM模型下,答案是不一定。原因就是CPU指令在执行时的重排序。在旧的JMM模型下,只规定了volatile变量和volatile变量之间不能进行重排序,但是并没有保证volatile变量和non-volatile变量之间不能进行重排序,所以, 当在thread A中,指令的执行可能是:

 

v = true;
x = 42;

 这样,当thread B 看到v为true的时候,x实际上还没有执行,所以值不是42.

 

 

   慢!眼尖的同学可能看出来了, 你说的这个跟重排序实际上没有关系呀,这个应该算是变量x的可见性问题,因为变量x不是声明为volatile的。

 

   好吧,我承认我偷懒了,在描述volatile变量可见性特质的时候,在新的JMM模型下,当对volatile变量进行写的时候,该线程(这里是thread A)所能看到变量(比如说变量x),都会一起刷新到主存中。这个也就是为什么我们会说对volatile变量的写操作,实际上等价于使用了synchronized关键字后释放monitor时产生的效果。 在这个前提下,上面的问题的确是CPU指令重排序的问题。

 

   但是幸运的是,JMM随后提出了happen-before原则来fix了这个问题(主要是volitale变量和non-volatile变量之间的重排序问题。)

    这里我只挑跟这个问题相关的三条原则来进行讲解,其余的可以到官方文档去查看。

 

    1. 单线程原则, 在单线程执行的环境下,指令的执行是跟程序代码的执行顺序一致。 对于上面的例子来说,在程序代码顺序上,x=42 先于 v=true, 那么在内存指令执行的时候也是如此。

 

    2. volatile变量原则,对volatile变量的写操作要优先于对volatile变量的读操作。

    3. 转递性原则,如果A操作先于B操作,B操作先于C操作,那么A操作肯定先于C操作。

 

    还是上面的例子,先用单线程原则,可以判断出,在thread A的执行中, x=42肯定要优先于v=true进行执行, 而在thread B的执行中,对v的读取操作肯定要优先于对x的使用操作。

    接着再使用volatile变量原则,可以判断,对v的写肯定要先于对v的读, 最后再根据转递性原则, 可以推出在thread A中x=42的赋值操作肯定要先于thread B中对x的使用, 也就是说,当v读取出来是为true的时候,x肯定是42. 指令不会进行重排序。

 

那如果我们将x=42,v=true的语句倒过来呢?

 

class VolatileExample {
  int x = 0;
  volatile boolean v = false;
  public void writer() {
    //颠倒赋值给x,v的顺序。
    v = true;
    x = 42;
  }

  public void reader() {
    if (v == true) {
      //uses x - can we see x is 42 here?
    }
  }
}
 

我想通过上面的分析,各位同学自己应该也能推断出来了吧。:)

 

 

0
3
分享到:
评论
2 楼 nurenok 2016-06-17  
1. 单线程原则, 在单线程执行的环境下,指令的执行是跟程序代码的执行顺序一致。 对于上面的例子来说,在程序代码顺序上,x=42 先于 v=true, 那么在内存指令执行的时候也是如此。

对于这个我也有疑问?很难确保把
1 楼 hobitton 2011-09-26  
有点疑问:
引用
1. 单线程原则, 在单线程执行的环境下,指令的执行是跟程序代码的执行顺序一致。 对于上面的例子来说,在程序代码顺序上,x=42 先于 v=true, 那么在内存指令执行的时候也是如此。


这个原则貌似没有见过呢?指令的执行顺序受编译器,JIT的影响,如果编译过程中进行了重排序,那么这段代码不可能知道运行的时候是否是单线程执行环境,那么这个原则就不对了吧?

相关推荐

    Volatile的陷阱.pdf

    本文将深入探讨volatile的含义、陷阱及其正确使用方式。 首先,volatile关键字的目的是告诉编译器,某个变量的值可能会在编译器无法察觉的情况下改变,例如由中断服务程序或并行硬件操作更改。这意味着每次访问该...

    Java并发编程(18)第五篇中volatile意外问题的

    在"Java并发编程(18)第五篇中volatile意外问题的正确分析解答"这篇文档中,作者深入探讨了这些问题并提供了详细的解决方案。 首先,我们要明确volatile的两个主要特性:一是保证了共享变量的可见性,即当一个线程...

    并发编程一之synchronized和volatile.rar

    - **禁止指令重排序**:volatile保证了内存语义的有序性,防止编译器和处理器进行过于激进的优化导致数据一致性问题。 - **不保证原子性**:虽然volatile可以保证可见性,但不能保证对变量的操作是原子性的,如int...

    简单了解java volatile关键字实现的原理

    下面我们将深入探讨`volatile`关键字的原理、使用场景以及与`synchronized`的区别。 一、`volatile`关键字的语义分析 1. **保证可见性**:当一个线程修改了`volatile`变量的值,其他所有线程都能立即看到这个变化...

    java 中volatile和lock原理分析

    本文将深入探讨这两种机制的原理,以及它们在实际编程中的应用。 首先,volatile关键字是Java中用于线程同步的一种轻量级机制。它主要有三个核心特性: 1. 可见性:当一个线程修改了volatile变量,其他所有线程都...

    day18_等待唤醒、JMM、并发编程特性、volatile.pdf

    1. ** volatile关键字**:volatile变量在JMM中有特殊的语义,它可以确保对volatile变量的修改对其他线程是立即可见的,避免了线程工作内存与主内存数据不一致的情况。在案例中,如果包子的状态(是否有包子)使用...

    浅谈C++中的mutable和volatile关键字

    下面是一些`volatile`关键字常见的使用场景: 1. **中断服务程序**:在中断服务程序中,我们可能需要修改一个变量来向其他线程或函数发送信号。这个变量需要是`volatile`的,因为它可能在中断处理程序执行期间被...

    04 并发编程专题06.zip

    "04 并发编程专题06.zip"这个压缩包文件包含了两个部分:"JMM&volatile详解(下)(1).vep"和"JMM&volatile详解(下)(2).vep",它们着重探讨了Java内存模型(JMM)以及volatile关键字的深入理解。 Java内存模型...

    java memory model

    - **volatile变量语义的加强**:在JSR-133之前,对volatile变量和非volatile变量的访问可以自由排序,但在新的规范中,volatile变量的操作具有了获取(acquire)和释放(release)语义,这意味着对volatile变量的...

    技术分享-JAVA并发库解读

    从重排序的概念出发,我们探讨了如何使用`volatile`关键字以及`final`域来确保多线程程序的一致性和可见性。此外,还讨论了CAS操作及其潜在的ABA问题,并提出了解决方案。这些知识点对于理解和开发高性能的多线程...

    深入理解Java内存模型

    JSR-133规范增强了volatile的内存语义,确保了volatile变量的有序性和可见性,这使得volatile在多线程程序中被广泛使用。 综上所述,Java内存模型是一个复杂的系统,它涉及了线程间的通信与同步、共享变量的访问...

    具有松弛依赖关系的C11程序的Owicki Gries推理(扩展版)_Owicki-Gries Reasoning for C1

    这篇论文"具有松弛依赖关系的C11程序的Owicki Gries推理(扩展版)"深入探讨了如何将这一推理方法应用到C11标准的程序中,这是C/C++的2011年版本,其内存模型相当复杂。 C11标准引入了弱内存模型,允许数据在不同...

    全网最硬核 Java 新内存模型解析与实验单篇版.doc

    Java 内存模型(JMM,Java Memory Model)是 Java 语言的核心组件之一,它定义了 Java 程序中内存的行为和语义。本文将深入探讨 JMM 的设计原理和实现机理,通过结合实际的代码实验和分析,帮助读者更好地理解 Java ...

    java内存屏障与JVM并发详解实用.pdf

    在本文中,我们将深入探讨Java内存屏障的概念、原理和应用,了解它在JVM并发机制中的重要作用。 一、内存屏障的定义和原理 内存屏障是一组处理器指令,用于实现对内存操作的顺序限制。它可以强制处理器按照特定的...

    各大公司校招笔试面试多线程和并发历年真题总结

    本文将详细探讨多线程和并发中的一些重要知识点,包括缓存一致性问题、volatile关键字的内存语义等,这些内容在各大公司的笔试面试中都是高频考点。 首先,让我们来解释一下什么是缓存一致性问题。当处理器执行计算...

    Effective.Modern.C++-42.Specific.Ways.to.Improve.Your.Use.of.C++11.and.C++14

    本书不仅介绍了C++11和C++14所引入的新特性,如自动类型推导、移动语义、lambda表达式和并发支持等,更深入讲解了如何有效地使用这些特性,以便编写出正确、高效、可维护和可移植的现代C++代码。 书中涵盖的知识点...

    java内存模型与并发技术.ppt

    此外,还有其他一些happens-before规则,如 volatile读写规则、初始化顺序规则等,它们共同保证了并发环境下的正确性。 了解这些基础知识后,开发者可以使用synchronized、volatile、final关键字以及ThreadLocal等...

    java高并发程序设计(原版电子书)

    9. **Java内存模型**:深入解析Java内存模型(JMM),理解数据一致性问题,如volatile的内存语义和happens-before原则。 10. **并发编程实战**:提供实际案例分析和练习,帮助读者将理论知识应用于实际项目,增强...

    java同步和内存模型

    在探讨Java的同步机制及其内存模型之前,我们首先理解一下为什么需要这两种机制。在单线程环境下,程序的执行通常按照顺序进行,编译器、处理器等底层硬件组件虽然会进行一定的优化,但最终程序的执行效果与代码的...

Global site tag (gtag.js) - Google Analytics