volatile让变量每次在使用的时候,都从主存中取。而不是从各个线程的“工作内存”。

volatile具有synchronized关键字的“可见性”,但是没有synchronized关键字的“并发正确性”,也就是说不保证线程执行的有序性。

也就是说,volatile变量对于每次使用,线程都能得到当前volatile变量的最新值。但是volatile变量并不保证并发的正确性。

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

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

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

 public class StoppableTask extends Thread {

   private volatile boolean pleaseStop;

   public void run() {

     while (!pleaseStop) {

      // do some stuff...

     }

  }

   public void tellMeToStop() {

    pleaseStop = true;

   }

 }

假如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代码:

 int i1;
int geti1() {return i1;}
volatile int i2;
int geti2()
{return i2;}
int i3;
synchronized int geti3() {return i3;}
  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就不是原子级别的:

 package  mythread;

 public   class  JoinThread  extends  Thread
{
public static volatile int n = 0 ;
public void run()
{
for ( int i = 0 ; i < 10 ; i ++ )
try
{
n = n + 1 ;
sleep( 3 ); // 为了使运行结果更随机,延迟3毫秒 }
catch (Exception e)
{
}
} public static void main(String[] args) throws Exception
{ Thread threads[] = new Thread[ 100 ];
for ( int i = 0 ; i < threads.length; i ++ )
// 建立100个线程
threads[i] = new JoinThread();
for ( int i = 0 ; i < threads.length; i ++ )
// 运行刚才建立的100个线程
threads[i].start();
for ( int i = 0 ; i < threads.length; i ++ )
// 100个线程都执行完后继续
threads[i].join();
System.out.println( " n= " + JoinThread.n);
}
}

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

n  =  n  +   1 ; 
n ++ ;

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

package  mythread;

public   class  JoinThread  extends  Thread
{
public static int n = 0 ; public static synchronized void inc()
{
n ++ ;
}
public void run()
{
for ( int i = 0 ; i < 10 ; i ++ )
try
{
inc(); // n = n + 1 改成了 inc();
sleep( 3 ); // 为了使运行结果更随机,延迟3毫秒 }
catch (Exception e)
{
}
} public static void main(String[] args) throws Exception
{ Thread threads[] = new Thread[ 100 ];
for ( int i = 0 ; i < threads.length; i ++ )
// 建立100个线程
threads[i] = new JoinThread();
for ( int i = 0 ; i < threads.length; i ++ )
// 运行刚才建立的100个线程
threads[i].start();
for ( int i = 0 ; i < threads.length; i ++ )
// 100个线程都执行完后继续
threads[i].join();
System.out.println( " n= " + JoinThread.n);
}
}

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

本文转载:http://sakyone.iteye.com/blog/668091

Java中volatile的作用以及用法的更多相关文章

  1. Java中Volatile的作用

    Java中Volatile的作用 看了几篇博客,发现没搞懂.可是简单来说,就是在我们的多线程开发中.我们用Volatile关键字来限定某个变量或者属性时,线程在每次使用变量的时候.都会读取变量改动后的 ...

  2. Java中static变量作用和用法详解

    static表示"全局"或者"静态"的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念. 被static ...

  3. Java中Volatile关键字详解

    一.基本概念 先补充一下概念:Java并发中的可见性与原子性 可见性: 可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉.通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值, ...

  4. 【Java学习笔记之二十九】Java中的"equals"和"=="的用法及区别

    Java中的"equals"和"=="的用法及区别 在初学Java时,可能会经常碰到下面的代码: String str1 = new String(" ...

  5. Java并发-volatile的原理及用法

    Java并发-volatile的原理及用法 volatile属性:可见性.保证有序性.不保证原子性.一.volatile可见性 在Java的内存中所有的变量都存在主内存中,每个线程有单独CPU缓存内存 ...

  6. java中volatile关键字的理解

    一.基本概念 Java 内存模型中的可见性.原子性和有序性.可见性: 可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉.通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有 ...

  7. Java中Volatile关键字详解(转载)

    转载自:https://www.cnblogs.com/zhengbin/p/5654805.html 一.基本概念 先补充一下概念:Java 内存模型中的可见性.原子性和有序性. 可见性: 可见性是 ...

  8. Java中Volatile关键字详解 (转自郑州的文武)

    java中volatile关键字的含义:http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html 一.基本概念 先补充一下概念:J ...

  9. Linq中关键字的作用及用法

    Linq中关键字的作用及用法 1.All:确定序列中的所有元素是否都满足条件.如果源序列中的每个元素都通过指定谓词中的测试,或者序列为空,则为 true:否则为 false. Demo: 此示例使用 ...

随机推荐

  1. ##DAY14——StoryBoard

    •iOS下可视化编程分为两种⽅式:xib和storyboard. •在使用xib和storyboard创建GUI过程中,以XML文件格式 存储在Xcode中,编译时生成nib的二进制文件.在运行时, ...

  2. Windows窗口消息大全(转)

    Windows窗口消息大全,全不全自己看 ////////////////////////////////////////////////////////////////////////// #inc ...

  3. Log4net 自定义字段到数据库

    今天要求做个log4net自定义字段到数据库,在网上找了好多例子,都运行不成功.最后找了个国外的,很简单的就解决了. log4net它已经定义的字段有 <commandText value=&q ...

  4. php-Eclipse对php中的namespace关键字报语法错误的问题

    namespace是php5.3以上才支持的, 解决办法:升级IDE 切换Eclipse中的php版本 Window  > Preferences  > PHP  > PHP Int ...

  5. hdu 4735Little Wish~ lyrical step~ 重复覆盖

    题目链接 给出一棵树, 树上点的值为0或1, 可以交换树上两个点的权值, 给出一个距离m, 所有的0距离最近的1的距离不能超过m, 求最少的交换次数. 首先对于每一个点u,所有离u的距离不超过m的点v ...

  6. 用python 10min手写一个简易的实时内存监控系统

    简易的内存监控系统 本文需要有一定的python和前端基础,如果没基础的,请关注我后续的基础教程系列博客 文章github源地址,还可以看到具体的代码,喜欢请在原链接右上角加个star 腾讯视频链接 ...

  7. Node爬虫

    Node爬虫 参考 http://www.cnblogs.com/edwardstudy/p/4133421.html 所谓的爬虫就是发送请求,并将响应的数据做一些处理 只不过不用浏览器来发送请求 需 ...

  8. Initialization failed for block pool Block pool(转载)

    2014-06-18 20:34:59,622 FATAL org.apache.hadoop.hdfs.server.datanode.DataNode: Initialization failed ...

  9. HDU 1076 An Easy Task

    题解:枚举即可…… #include <cstdio> int main(){ int now,y,n,T,count; scanf("%d",&T); whi ...

  10. Highlight On Mouseover Effect With JQuery

    How to get the xpath by clicking an html element How to get the xpath by clicking an html element Qu ...