`

volatile和重排序得一些小疑问

    博客分类:
  • java
 
阅读更多

http://yeziwang.iteye.com/blog/1042492 

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

 

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

 

   1. 可见性。

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

 

 

   2. 禁止CPU指令的重排序

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

 

     来看下代码,

Java代码  收藏代码
  1. class VolatileExample {  
  2.   int x = 0;  
  3.   volatile boolean v = false;  
  4.   
  5.   //in thread A  
  6.   public void writer() {  
  7.     x = 42;  
  8.     v = true;  
  9.   }  
  10.   
  11.   //in thread B  
  12.   public void reader() {  
  13.     if (v == true) {  
  14.       //uses x - can we see x is 42?  
  15.     }  
  16.   }  
  17. }  

 

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

 

Java代码  收藏代码
  1. v = true;  
  2. 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的语句倒过来呢?

 

Java代码  收藏代码
  1. class VolatileExample {  
  2.   int x = 0;  
  3.   volatile boolean v = false;  
  4.   public void writer() {  
  5.     //颠倒赋值给x,v的顺序。  
  6.     v = true;  
  7.     x = 42;  
  8.   }  
  9.   
  10.   public void reader() {  
  11.     if (v == true) {  
  12.       //uses x - can we see x is 42 here?  
  13.     }  
  14.   }  
  15. }  

 

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

 

分享到:
评论

相关推荐

    volatile源码分析1

    本文将从JVM、C++以及汇编语言的角度深入探讨volatile的两大特性:禁止重排序和内存可见性。 1. 禁止重排序 重排序是指编译器和处理器为了优化性能而可能对指令序列进行重新排列的一种行为。在Java中,volatile...

    const和volatile分析

    在C++编程语言中,`const`和`volatile`是两个非常重要的关键字,它们用于修饰变量,赋予变量特殊的属性。这两个关键字在理解程序的行为、内存模型以及多线程编程中起到至关重要的作用。在此,我们将深入探讨`const`...

    volatile 变量的说明

    2. **禁止指令重排序**:编译器通常会对指令进行重排序以优化性能,但volatile变量的写操作后,后续读操作不能被提前,写操作前的读操作也不能被延后。这确保了对volatile变量的修改按照程序的顺序进行,防止出现...

    const extern static volatile 小结

    ### const extern static volatile 小结 #### 一、Const(常量) `const` 关键字在 C/C++ 语言中用于定义常量,即其值在程序运行期间不可更改的变量。`const` 可以与多种数据类型结合使用,例如 `const int x = 10...

    Volatile详解,深入学习Volatile

    《深入理解Volatile》 ...正确理解和使用volatile可以避免不必要的数据冲突,提高程序的正确性和可靠性。但在使用时,需要注意其局限性,特别是在多线程环境下,volatile并不能完全替代同步机制。

    volatile的用法讲解

    "volatile的用法讲解" volatile是一种特殊的变量修饰符,它告诉编译器,这个变量的值可能会被意外地改变,因此编译器不能对其进行优化,以确保每次读取该变量时都能获取最新的值。下面是volatile变量的使用场景: ...

    内存栅栏和volatile关键字1

    内存栅栏和volatile关键字在多线程编程中...volatile确保了变量的可见性和无重排序,而内存栅栏则提供了更精细的控制,防止指令重排序和缓存延迟。合理使用这些机制,可以在保证线程安全的同时,提升并发程序的性能。

    volatile变量详解

    在编程中,需要正确地理解 volatile 变量的作用和应用场景,以确保程序的正确性和效率。 此外,需要注意的是,volatile 变量的解释不应该是“易变的”,而是“直接存取原始内存地址”。“易变”是因为外在因素引起...

    volatile用法

    例如,在中断服务子程序中,如果指针指向的数据是共享的,那么指针和指针指向的数据都应该声明为`volatile`。 4. `volatile`的错误使用示例: 在提供的代码示例中,`square`函数有一个潜在的问题。因为它计算`*ptr...

    Volatile.pdf

    文档内容的其他部分提到了一些关键字和概念,例如“指令重排序”、“内存可见性”、“Java内存模型(JMM)”,这些都和volatile的正确使用与理解密切相关。在Java中,理解内存模型和相关的并发机制对于编写高性能且...

    volatile,nonatomic和atomic关键字测试

    在iOS和Mac开发中,Objective-C是主要的编程语言,它在定义实例变量或属性时提供了几个关键的修饰符,包括`volatile`、`nonatomic`和`atomic`。这些关键字对于理解多线程环境下的数据同步和内存管理至关重要。本文将...

    聊聊并发(一)深入分析Volatile的实现原理

    `LinkedTransferQueue`是基于链表结构的无界并发队列,它的内部节点使用`Node`类表示,`Node`中的一些字段(如`prev`、`next`)可能被声明为Volatile,以确保在多线程环境下节点之间的链接关系能被正确地同步和更新...

    java里的volatile关键字详解

    3. 有序性:Java语言提供了volatile 和synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized是由“一个变量在同一个时刻只允许一条线程对其进行...

    Java线程:volatile关键字

    Java™ 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量。volatile 变量的同步性较差,但它有时更简单并且开销更低。volatile 变量可以被看作是一种 “程度较轻的 synchronized”;与 synchronized ...

    volatile详解

    1. **const与volatile的结合使用**:一个变量可以同时声明为`const`和`volatile`。例如,只读的状态寄存器可以被声明为`const volatile int statusRegister;`。这里的`const`表明程序不应尝试修改这个寄存器的值,而...

    volatile使用详解

    - **编译器优化**:编译器通常会对代码进行优化,比如重排序,以提高程序性能。但这种优化有时会导致并发问题或硬件访问问题。使用 `volatile` 可以阻止这种优化行为,保证代码的正确执行顺序。 #### 三、Volatile ...

    volatile的用法

    接下来详细说明volatile的用法和相关知识点: 1. 中断服务程序中的用法:当中断服务程序修改一个变量时,主程序需要检测这个变化。如果这个变量没有声明为volatile,编译器可能会优化掉变量的读取操作,只从寄存器...

    一文精通Java中的volatile关键字

    Java中的`volatile`关键字是多线程编程中的一个重要概念,它的主要作用是确保共享变量的可见性和禁止指令重排序。本文将深入探讨`volatile`的关键特性、工作原理以及使用注意事项。 1. 可见性: `volatile`关键字...

Global site tag (gtag.js) - Google Analytics