Java之Volatile关键字使用

1.为什么要使用Volatile关键字?

  先来看看一段代码:

package com.zy;
 
importjava.util.concurrent.TimeUnit;
 
public class VolatileTest {
   private static boolean isRuning = true;
   public static void main(String[] args) {
       new Thread(new Runnable() {
          @Override
          public void run() {
              int i = 0;
              while(VolatileTest.isRuning){
                 i++;
              }
          }
       }).start();
       try {
          TimeUnit.SECONDS.sleep(1);
       } catch(InterruptedException e) {
          e.printStackTrace(); 
       }
       new Thread(new Runnable() {
          @Override
          public void run() {
              isRuning = false;  //设置is为false,使上面的线程结束while循环
              System.out.println("设置isRuning为:false");
          }
       }).start();
   }
}


      代码很简单,启动两个线程,在主类中定一个一个全局的成员变量isRuning,线程一启动只要isRunning为true将持续i++,线程2将isRunning置为false,按照正常逻辑此时线程1也将停止,因为while中条件不成立了嘛。然而真是这样吗?

      答案显然是:NO,整个程序依然在运行。

相信这种代码肯定有很多人写过,犯过这种错的人也不再少数。废话不多说,那么究竟是为什么会发生这种情况呢,我只给出比较浅显的解释,学艺有限,不再深究。

要解释这个问题就要从java线程的内存分配讲起,先来看一张(原文链接:http://www.cnblogs.com/nexiyi/p/java_memory_model_and_thread.html

     Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样底层细节。此处的变量与Java编程时所说的变量不一样,指包括了实例字段、静态字段和构成数组对象的元素,但是不包括局部变量与方法参数,后者是线程私有的,不会被共享。

     Java内存模型中规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存(可以与前面将的处理器的高速缓存类比),线程的工作内存中保存了该线程使用到的变量到主内存副本拷贝,线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递均需要在主内存来完成,线程、主内存和工作内存的交互关系如下图所示,和上图很类似。

相信读了上面一段话大家也就大概明白了,线程中获取到所有的变量(示例程序中的isRunning)实际上只主内存的一个副本,线程2改变isRunning的值只是改变了副本的值,此时线程1如果要发现isRunning的值改变,首先线程2要将isRunning的值刷新的主内存,然后线程1要重新刷新isRunning的值。

至于线程中的变量副本的值如何刷新和何时刷新到主内存,和如何和何时从主内存中重新load值,本文不做概述。

所以说不同线程中的变量是不可见的!!!

2. Volatile作用

     此时Volatile关键字出场了,它正是用来解决不同线程变量的可视性问题的。

     如果你将一个变量申明Volatile的,那么只要对这个变量做出更改,那么其他的所有读操作就会看到这个更改。即使使用了本地缓存,情况也是如此,volatile所修饰的变量会立即被写入到主内存,而读操作就发生在主内存中。

3.Volatile使用场景

     多个线程同时访问某个变量,那么这个变量就应该是volatile的,否则这个变量只能用Synchronize来同步访问。如果一个方法或者代码块完全是synchronize的,那么就不要volatile来修饰,因为同步方法数据会立即写入主内存,同时方法取值也是直接在主内存中取。

4.使用条件

        4.1:一个变量值不依赖于它之前的值。比如:递增的计数器,++i

        4.2:这个值不受其他变量值得限制。比如:Range类中的lower和upper边界必须遵守lower<upper

        以上条件应该是要保证volatile变量的操作是原子操作。(何为原子操作:原子操作是不能被线程调度机制中断的操作,一旦操作开始,那么它一定可以在可能发生上下文切换之前(切换到其他线程之前)完成操作。)如果不是原子操作的话在完成一个操作中间可能包含多个指令,而这中间就可能发生上下文切换。在切换时其中的值已经被其他任务修改,此时再切换回来执行最后一条指令,所读取到的变量值可能已经被其他任务修改过。

 

 如有不足之处请指出,菜鸟一枚,轻喷。微笑微笑

参考资料:

   java编程思想第四版

   http://www.cnblogs.com/nexiyi/p/java_memory_model_and_thread.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值