java中volatitle关键字的作用

用在多线程,同步变量。 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B。只在某些动作时才进行A和B的同步。因此存在A和B不一致 的情况。volatile就是用来避免这种情况的。volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A)

=========================分割线1=================================

版权声明 :转载时请以超链接形式标明文章原始出处和作者信息及本声明

http://aleung.blogbus.com/logs/32090434.html

在Java内存模型中,有main memory,每个线程也有自己的memory
(例如寄存器)。为了性能,一个线程会在自己的memory中保持要访问的变量的副本。这样就会出现同一个变量在某个瞬间,在一个线程的memory中的
值可能与另外一个线程memory中的值,或者main memory中的值不一致的情况。

一个变量声明为volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory中。以下例子展现了volatile的作用:

Java代码  

  1. public class StoppableTask extends Thread {
  2. private volatile boolean pleaseStop;
  3. public void run() {
  4. while (!pleaseStop) {
  5. // do some stuff...
  6. }
  7. }
  8. public void tellMeToStop() {
  9. pleaseStop = true;
  10. }
  11. }

假如pleaseStop没有被声明为volatile,线程执行run的时候检查的是自己的副本,就不能及时得知其他线程已经调用tellMeToStop()修改了pleaseStop的值。

Volatile一般情况下不能代替sychronized,因为volatile不能保证操作的原子性,即使只是i++,实际上也是由多个原子
操作组成:read i; inc; write
i,假如多个线程同时执行i++,volatile只能保证他们操作的i是同一块内存,但依然可能出现写入脏数据的情况。如果配合Java
5增加的atomic wrapper classes,对它们的increase之类的操作就不需要sychronized。

Reference:

http://www.javamex.com/tutorials/synchronization_volatile.shtml

http://www.javamex.com/tutorials/synchronization_volatile_java_5.shtml

http://www.ibm.com/developerworks/cn/java/j-jtp06197.html

=========================分割线2=================================

恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。volatile是变量修饰符,而synchronized则作用于一段代码或方法;看如下三句get代码:

Java代码  

  1. int i1;
  2. int geti1() {return i1;}
  3. volatile int i2;
  4. int geti2()
  5. {return i2;}
  6. int i3;
  7. synchronized int geti3() {return i3;}
  8.   geti1()

得到存储在当前线程中i1的数值。多个线程有多个i1变量拷贝,而且这些i1之间可以互不相同。换句话说,另一个线程可能已经改变了它线程内的
i1值,而这个值可以和当前线程中的i1值不相同。事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程可以有它自己的
变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的i1值是1,线程1里的i1值是2,线程2里
的i1值是3——这在线程1和线程2都改变了它们各自的i1值,而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。

  而
geti2()得到的是“主”内存区域的i2数值。用volatile修饰后的变量不允许有不同于“主”内存区域的变量拷贝。换句话说,一个变量经
volatile修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。理所当然的,volatile修饰的变量
存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。

  既然volatile关键字已经实现了线程间数据同步,又要
synchronized干什么呢?呵呵,它们之间有两点不同。首先,synchronized获得并释放监视器——如果两个线程使用了同一个对象锁,监
视器能强制保证代码块同时只被一个线程所执行——这是众所周知的事实。但是,synchronized也同步内存:事实上,synchronized在“
主”内存区域同步整个线程的内存。因此,执行geti3()方法做了如下几步:

1. 线程请求获得监视this对象的对象锁(假设未被锁,否则线程等待直到锁释放)

2. 线程内存的数据被消除,从“主”内存区域中读入(Java虚拟机能优化此步。。。[后面的不知道怎么表达,汗])

3. 代码块被执行

4. 对于变量的任何改变现在可以安全地写到“主”内存区域中(不过geti3()方法不会改变变量值)

5. 线程释放监视this对象的对象锁

  因此volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。显然synchronized要比volatile消耗更多资源。

=========================分割线3=================================

volatile关键字相信了解Java多线程的读者都很清楚它的作用。volatile关键字用于声明简单类型变量,如int、float、
boolean等数据类型。如果这些简单数据类型声明为volatile,对它们的操作就会变成原子级别的。但这有一定的限制。例如,下面的例子中的n就
不是原子级别的:

Java代码  

  1. package  mythread;
  2. public   class  JoinThread  extends  Thread
  3. {
  4. public   static volatile int  n  =   0 ;
  5. public   void  run()
  6. {
  7. for  ( int  i  =   0 ; i  <   10 ; i ++ )
  8. try
  9. {
  10. n  =  n  +   1 ;
  11. sleep( 3 );  //  为了使运行结果更随机,延迟3毫秒
  12. }
  13. catch  (Exception e)
  14. {
  15. }
  16. }
  17. public   static   void  main(String[] args)  throws  Exception
  18. {
  19. Thread threads[]  =   new  Thread[ 100 ];
  20. for  ( int  i  =   0 ; i  <  threads.length; i ++ )
  21. //  建立100个线程
  22. threads[i]  =   new  JoinThread();
  23. for  ( int  i  =   0 ; i  <  threads.length; i ++ )
  24. //  运行刚才建立的100个线程
  25. threads[i].start();
  26. for  ( int  i  =   0 ; i  <  threads.length; i ++ )
  27. //  100个线程都执行完后继续
  28. threads[i].join();
  29. System.out.println( " n= "   +  JoinThread.n);
  30. }
  31. }

如果对n的操作是原子级别的,最后输出的结果应该为n=1000,而在执行上面积代码时,很多时侯输出的n都小于1000,这说明n=n+1不是原子级别
的操作。原因是声明为volatile的简单变量如果当前值由该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式都不是原子
操作:

n  =  n  +   1 ;

n ++ ;

如果要想使这种情况变成原子操作,需要使用synchronized关键字,如上的代码可以改成如下的形式:

Java代码  

  1. package  mythread;
  2. public   class  JoinThread  extends  Thread
  3. {
  4. public   static int  n  =   0 ;
  5. public static   synchronized   void  inc()
  6. {
  7. n ++ ;
  8. }
  9. public   void  run()
  10. {
  11. for  ( int  i  =   0 ; i  <   10 ; i ++ )
  12. try
  13. {
  14. inc();  //  n = n + 1 改成了 inc();
  15. sleep( 3 );  //  为了使运行结果更随机,延迟3毫秒
  16. }
  17. catch  (Exception e)
  18. {
  19. }
  20. }
  21. public   static   void  main(String[] args)  throws  Exception
  22. {
  23. Thread threads[]  =   new  Thread[ 100 ];
  24. for  ( int  i  =   0 ; i  <  threads.length; i ++ )
  25. //  建立100个线程
  26. threads[i]  =   new  JoinThread();
  27. for  ( int  i  =   0 ; i  <  threads.length; i ++ )
  28. //  运行刚才建立的100个线程
  29. threads[i].start();
  30. for  ( int  i  =   0 ; i  <  threads.length; i ++ )
  31. //  100个线程都执行完后继续
  32. threads[i].join();
  33. System.out.println( " n= "   +  JoinThread.n);
  34. }
  35. }

上面的代码将n=n+1改成了inc(),其中inc方法使用了synchronized关键字进行方法同步。因此,在使用volatile关键字时要慎
重,并不是只要简单类型变量使用volatile修饰,对这个变量的所有操作都是原来操作,当变量的值由自身的上一个决定时,如n=n+1、n++
等,volatile关键字将失效,只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的,如n = m +
1,这个就是原级别的。所以在使用volatile关键时一定要谨慎,如果自己没有把握,可以使用synchronized来代替volatile。

时间: 2024-10-25 22:17:19

java中volatitle关键字的作用的相关文章

Java中Native关键字的作用

阅读目录 一.认识 native 即 JNI,Java Native Interface 二.用 Java 调用 C 的"Hello,JNI" 三.JNI 调用 C 流程图 四.其他介绍 初次遇见 native是在 java.lang.Object 源码中的一个hashCode方法: 1 public native int hashCode(); 为什么有个native呢?这是我所要学习的地方.所以下面想要总结下native. 回到顶部 一.认识 native 即 JNI,Java N

java中static关键字的作用

在java语言中有四种使用情况:成员变量.成员方法.代码块和内部类 (1)static成员变量 java中可以通过statin关键字修饰变量达到全局变量的效果.static修饰的变量(静态变量)属于类,在类第一次通过类加载器到jvm时被分配内存空间. (2)static成员方法 static修饰的方法属于类方法,不需要创建对象就可以调用.static方法中不能使用this和super等关键字,不能调用非static方法,只能访问所属类的静态成员变量和静态方法. (3)static 代码块 JVM

Java中super关键字的作用与用法

Java中的super是什么?java中的super关键字是一个引用变量,用于引用父类对象.关键字“super”以继承的概念出现在类中.主要用于以下情况: 1.使用super与变量:当派生类和基类具有相同的数据成员时,会发生此情况.在这种情况下,JVM可能会模糊不清.我们可以使用以下代码片段更清楚地理解它: /* Base class vehicle */ class Vehicle { int maxSpeed = 120; } /* sub class Car extending vehic

java中this关键字的作用

一.this关键字主要有三个应用: (1)this调用本类中的属性,也就是类中的成员变量: (2)this调用本类中的其他方法: (3)this调用本类中的其他构造方法,调用时要放在构造方法的首行. Public Class Student { String name; //定义一个成员变量name private void SetName(String name) { //定义一个参数(局部变量)name this.name=name; //将局部变量的值传递给成员变量 } } 应用一:引用成

JAVA中 this关键字的作用

this关键字主要有三个应用:1.this调用本类中的属性,也就是类中的成员变量:2.this调用本类中的其他方法:3.this调用本类中的其他构造方法,调用时要放在构造方法的首行.Public Class Student {   String name; //定义一个成员变量name  private void SetName(String name) { //定义一个参数(局部变量)name  this.name=name; //将局部变量的值传递给成员变量  }}应用一:引用成员变量 如上

Java中static关键字的作用和用法详细介绍

static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念. 被static修饰的成员变量和成员方法独立于该类的任何对象.也就是说,它不依赖类特定的实例,被类的所有实例共享. 只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们.因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象. 用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类

Java中volatile关键字的作用

在Java内存模型中,有main memory(主内存)还每个线程各自的线程内存memory(例如:寄存器).为了性能一个线程会在自己memory中保持要访问变量的副本.这样就会出现同一个变量在某一个时刻一个线程内存中的值和其他线程内存或者主内存中的值不一致. 一个变量声明为volatile,就意味着这个变量随时会被其他线程修改,因此不能将他cahe在线程memory中,即:不会再memory中保留他的副本,下面看个例子: public class TestVolatile extends Th

深入解析Java中volatile关键字的作用

Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制. synchronized 同步块大家都比较熟悉,通过 synchronized 关键字来实现,所有加上synchronized 和

java中super关键字的作用

1.super关键字可以在子类的构造方法中显示地调用父类的构造方法,super()必须为子类构造函数中的第一行. 2.super可以用来访问父类的成员方法或变量,当子类的方法或成员变量与父类有相同的名字时也会覆盖父类的成员变量或方法,这个时候要想访问父类的成员变量或方法只能通过super关键字来访问,子类方法中的super.方法名()不需要位于第一行. package test; class FatherClass { public int value; public void f() { va