Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3350950
  • 博文数量: 530
  • 博客积分: 13360
  • 博客等级: 上将
  • 技术积分: 5473
  • 用 户 组: 普通用户
  • 注册时间: 2006-07-13 13:32
文章分类

全部博文(530)

文章存档

2017年(1)

2015年(2)

2013年(24)

2012年(20)

2011年(97)

2010年(240)

2009年(117)

2008年(12)

2007年(8)

2006年(9)

分类: Java

2011-04-01 17:43:20

1.Volatile产生原因
      Java中设置变量值的操作,除了long和double类型的变量外都是原子操作,也就是说,对于变量值的简单读写操作没有必要进行同步。这在JVM 1.2之前,Java的内存模型实现总是从主存读取变量,是不需要进行特别的注意的。而在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。

      Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。Volatile其实是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我

      volatile的覆盖范围仅仅变量级别的,它的同步代价很低.。Volatile 读操作开销非常低 —— 几乎和非 volatile 读操作一样。而 volatile 写操作的开销要比非 volatile 写操作多很多,因为要保证可见性需要实现内存界定(Memory Fence),即便如此,volatile 的总开销仍然要比锁获取低。

2.Volatile不能使用场景
(1) volatile 变量不能用作线程安全计数器
      虽然增量操作(x++)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。(如果将值调整为只从单个线程写入,则可以使用)
  1. public class TestRaceCondition {
  2.         private volatile int i = 0;
  3.       
  4.         public void increase() {
  5.            i++;
  6.         }
  7.      
  8.         public int getValue() {
  9.            return i;
  10.        }
  11.    }
当多线程执行increase方法时, 不能保证它的值会是线性递增的。

3.Volatile可以使用场景
      使用 volatile 变量替代锁,要始终牢记使用 volatile 的限制
             —— 只有在状态真正独立于程序内其他内容时才能使用 volatile

3.1状态标志
       volatile 布尔型变量作为状态标志,用于指示发生了一个重要的一次性事件,例如完成初始化或请求停机。很多应用程序包含了一种控制结构,形式为 “在还没有准备好停止程序时再执行一些工作”。

代码:将 volatile 变量作为状态标志使用
  1. /**
  2.  * 将 volatile 变量作为状态标志使用.
  3.  *
  4.  * @version V1.0 ,2011-4-2
  5.  * @author xiahui
  6.  *
  7.  */
  8. public class VolatileThread1 extends Thread
  9. {
  10.      private int count=0;
  11.      public static volatile boolean shutdownRequested=false;
  12.     
  13.      public void shutdown(){
  14.          shutdownRequested = true;
  15.     }
  16.     
  17.     public void run(){
  18.      while (!shutdownRequested){
  19.      System.out.println(this.getName()+(++count));
  20.      try{
  21.      sleep(100);
  22.      }catch(InterruptedException ex){
  23.      }
  24.      }
  25.      }
  26.     
  27.    public static void main(String[] args) throws Exception
  28.    {
  29.      VolatileThread1 counter1 = new VolatileThread1();
  30.      VolatileThread1 counter2 = new VolatileThread1();
  31.      VolatileThread1 counter3 = new VolatileThread1();
  32.        
  33.        counter1.setName("counter1_");
  34.        counter2.setName("counter2_");
  35.        counter3.setName("c3_");
  36.        
  37.        counter1.start();
  38.        counter2.start();
  39.        counter3.start();
  40.        
  41.        sleep(500); // 主线程延迟
  42.        counter3.shutdown();
  43.        System.out.println(Thread.currentThread().getName()+":shutdown");
  44.    }
  45. }
运行结果
  1. counter1_1
  2. counter2_1
  3. c3_1
  4. counter1_2
  5. counter2_2
  6. c3_2
  7. counter1_3
  8. counter2_3
  9. c3_3
  10. counter1_4
  11. counter2_4
  12. c3_4
  13. counter1_5
  14. counter2_5
  15. c3_5
  16. main:shutdown
注意:如果变量shutdownRequested不是静态的,则程序只能中止counter3线程,因为每个线程类的变量是私有的。

例2:该类用于
  1. public class VolatileFlag {
  2.     
  3.     int x = 0;
  4.     public volatile boolean v = false;
  5.     public void writer() {
  6.      x = 42;
  7.      v = true;
  8.      }

  9.      public void reader() {
  10.      if (v == true) {
  11.            //uses x - guaranteed to see 42.
  12.           System.out.println(" value="+x);
  13.      }
  14.      else
  15.          System.out.println("value="+x);
  16.      }
  17.     }
运行类
  1. public class VolatileThread2 extends Thread {
  2.     private int count = 0;
  3.     
  4.     public VolatileFlag flag = null;

  5.     public static volatile boolean shutdownRequested=false;
  6.     
  7.      public void shutdown(){
  8.          shutdownRequested = true;
  9.     }
  10.     public void run() {
  11.         
  12.         while (!shutdownRequested) {
  13.             System.out.print(this.getName() + (++count));
  14.             flag.reader();
  15.             try {
  16.                 sleep(100);
  17.             } catch (InterruptedException ex) {
  18.             }
  19.         }
  20.     }

  21.     public static void main(String[] args) throws Exception{
  22.         
  23.      VolatileFlag flag=new VolatileFlag();
  24.      VolatileThread2 counter1 = new VolatileThread2();
  25.      VolatileThread2 counter2 = new VolatileThread2();
  26.      VolatileThread2 counter3 = new VolatileThread2();

  27.      //三个线程共用一个对象
  28.      counter1.flag=flag;
  29.      counter2.flag=flag;
  30.      counter3.flag=flag;
  31.     
  32.       counter1.setName("counter1_");
  33.       counter2.setName("counter2_");
  34.       counter3.setName("counter3_");
  35.       
  36.       counter1.start();
  37.       counter2.start();
  38.       counter3.start();
  39.       
  40.       sleep(3000); // 主线程延迟
  41.       flag.writer();//主线程给出写操作
  42.       sleep(200); // 主线程延迟
  43.       counter3.shutdown();
  44.       System.out.println(Thread.currentThread().getName()+":shutdown"+" value="+flag.x);
  45.   }
  46. }

3.2开销较低的读-写锁策略
      如果读操作远远超过写操作,您可以结合使用内部锁和 volatile 变量来减少公共代码路径的开销。如果更新不频繁的话,该方法可实现更好的性能,因为读路径的开销仅仅涉及 volatile 读操作,这通常要优于一个无竞争的锁获取的开销。
  1. public class CheesyCounter {
  2.     private volatile int value;
  3.     public int getValue() { return value; }
  4.     public synchronized int increment() {
  5.         return value++;
  6.     }
  7. }
使用 synchronized 确保增量操作是原子的 ,使用 volatile 保证当前结果的可见性。



参考文献
1.JAVA 里volatile关键字. http://www.cnblogs.com/phpzxh/archive/2009/12/15/1624709.html
2.Java 理论与实践: 正确使用 Volatile 变量.http://www.ibm.com/developerworks/cn/java/j-jtp06197.html
阅读(1125) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~