码迷,mamicode.com
首页 > 编程语言 > 详细

java并发编程 -volatile关键字

时间:2015-04-28 22:54:20      阅读:235      评论:0      收藏:0      [点我收藏+]

标签:volatile   并发   编程   

java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他的线程。

当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序,volatile变量 不会被缓存在寄存器或者对处理器不可见的地方,因此在读取volatile变量时总会返回最新写入的值。访问volatile变量不会执行加锁操作,因此也就不会使得执行线程阻塞,因此volatile变量是一种比sychronized关键字更加轻量级的同步机制。

一种volatile变量典型的用法:检查某个状态标记以判断是否退出循环

volatile boolean asleep;

while( ! asleep)
       countSomeSheep();

volatile变量通常用着某个操作完成、发生中断或者状态的标识。尽管volatile变量可以用于表示其他的状态信息,但是在使用时要非常小心。例如,volatile的语义不足以保证递增操作(count++)的原子性,除非你能确保只有一个线程对变量进行写操作。

加锁机制既可以保证可见性又可以保证原子性,而volatile变量只能保证可见性。

使用volatile应满足的一些条件

  • 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值
  • 该变量不会与其他状态变量一起纳入不变性条件中
  • 在访问变量是不需要加锁



如和理解上面的三条,首先看第一条

对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值

常见的操作如:i++操作,i的写入需要依赖i自身的值,当有多个线程同时执行i++时,A,B读了i的值,然后进行++操作,实际上得到的值可能只++了一次

如下代码:

public class Volatiletest extends Thread {

        static volatile int a=0;


      public void run()
      {

         for  ( int   i  =   0 ; i  <   10 ; i ++ ) 
              try  
         { 
                 a  =  a  +   1 ; 
                 sleep( 300 );    

             } 
              catch  (Exception e) 
             { 
             } 
      }

      public static void main(String []args) throws InterruptedException
      {
         Thread thread[]= new Thread[100];

         for(int i=0;i<100;i++)
                thread[i]= new Volatiletest();

         for(int i=0;i<100;i++)
                thread[i].start();

         for(int i=0;i<100;i++)
                thread[i].join();

         System. out.println("a:" +a);

      }
}

运行,可以得知a的结果并不一定等于1000,很多时候要小于1000



第二条:该变量不会与其他状态变量一起纳入不变性条件中

看一个例子:有范围值 lower总是小于等于upper 这是一个不变式

  public class NumberRange{
           private volatile int lower ,upper ;
           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;
           }
       }

这种方式限制了范围的状态变量,因此将 lower 和 upper 字段定义为 volatile 类型不能够充分实现类的线程安全;从而仍然需要使用同步。否则,如果凑巧两个线程在同一时间使用不一致的值执行 setLower 和 setUpper 的话,则会使范围处于不一致的状态。例如,如果初始状态是 (0,5),同一时间内,线程 A 调用 setLower⑷ 由于用的是volatile变量,那么读取的upper为5,并且线程 B 调用 setUpper⑶同理,由于用的是volatile变量,读取的lower为0,显然这两个操作交叉存入的值是不符合条件的,那么两个线程都会通过用于保护不变式的检查,使得最后的范围值是 (4,3) —— 一个无效值。至于针对范围的其他操作,我们需要使 setLower() 和 setUpper() 操作原子化 —— 而将字段定义为 volatile 类型是无法实现这一目的的。



第三条:由前面可以知道—加锁机制既可以保证可见性又可以保证原子性,而volatile变量只能保证可见性。

所以volatile变量并不能保证加锁操作的原子性

java并发编程 -volatile关键字

标签:volatile   并发   编程   

原文地址:http://blog.csdn.net/yujin753/article/details/45342955

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!