转载:http://blog.csdn.net/orzorz/article/details/4319055

用在多线程,同步变量。 线程为了提高效率,将某成员变量(如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的作用:
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:

=========================分割线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 
+
 
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。

java中关键字volatile的作用(转载)的更多相关文章

  1. java中关键字volatile的作用

    用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况.volatile就是用来 ...

  2. 转!!java中关键字volatile的作用

    用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况.volatile就是用来 ...

  3. Java中的volatile的作用和synchronized作用

    volatile该关键字是主要使用的场合是字啊多个线程中可以感知实例的变量被更改了并且可以获取到最新的值进行使用,也就是用多线程读取共享变量的时候可以获取到最新的值使用.不能保障原子性 如果你在jvm ...

  4. java中关键字volatile的误解和使用

    在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言是支持多线程的,为了解决线程并发的问题,在语 ...

  5. java多线程关键字volatile的使用

    java多线程关键字volatile的作用是表示多个线程对这个变量共享. 如果是只读的就可以直接用,写数据的时候要注意同步问题. 例子: package com.ming.thread.volatil ...

  6. 一定要你明白Java中的volatile

    今天Tony来和大家聊聊Java中关键字volatile. 字节码 首先volatile int a = 3;和int a = 3;, 加不加volatile关键字,最终生成的字节码都一样的.有兴趣的 ...

  7. java中的volatile关键字

    java中的volatile关键字 一个变量被声明为volatile类型,表示这个变量可能随时被其他线程改变,所以不能把它cache到线程内存(如寄存器)中. 一般情况下volatile不能代替syn ...

  8. Java中的volatile关键字的功能

    Java中的volatile关键字的功能 volatile是java中的一个类型修饰符.它是被设计用来修饰被不同线程访问和修改的变量.如果不加入volatile,基本上会导致这样的结果:要么无法编写多 ...

  9. 深入理解Java中的volatile关键字

    在再有人问你Java内存模型是什么,就把这篇文章发给他中我们曾经介绍过,Java语言为了解决并发编程中存在的原子性.可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized ...

随机推荐

  1. python argparse:命令行参数解析详解

    简介 本文介绍的是argparse模块的基本使用方法,尤其详细介绍add_argument内建方法各个参数的使用及其效果. 本文翻译自argparse的官方说明,并加上一些笔者的理解 import a ...

  2. Mycat分布式数据库架构解决方案--Mycat实现读写分离

    echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!!! 安装完 ...

  3. 学习笔记48_Memcache跟用户登录模块结合

    public interface ICacheWriter {  void AddCache(string key,object value, DateTime expDate);  void Add ...

  4. mysql设计规范二

    一.基本规范 必须使用InnoDB存储引擎 必须使用UTF8字符集 数据表.数据字段必须加入中文注释 二.设计规范 库名称.表名称.字段名称必须使用小写,最好不要使用驼峰式,使用“_”区分,例如use ...

  5. QA:无重复字符的最长子串

    无重复字符的最长字串 leetcode地址:https://leetcode-cn.com/problems/longest-substring-without-repeating-character ...

  6. [考试反思]1006csp-s模拟测试62:隔断

    本来说好的好一场烂一场. 那样的日子结束了,连着烂了两场...幸亏T3傻逼了救我一命不算太惨... T1树上的特殊性质会做但是没有继续想下去就死在错贪心上了还没有过那个点... T2迭代至稳定被我错误 ...

  7. CSPS模拟 46

    勿忘国耻. 由于重新评测我看到了不是很真实的一幕 紧接着是更不真实的一幕 就在虚假形象快要建立完成的时候 它由于来自东方的神秘力量倒塌了 被两个学校的大佬爆踩了(捂脸 T1 无脑背包? 考试时想1h想 ...

  8. EffectiveJava-2

    一.使用类库 使用类库的好处: 无须关心方法是如何实现的,由算法专家花了大量时间设计.实现和测试这个方法,不仅保证了正确性,而且一旦有缺陷,下一个版本就会修复. 不必浪费时间为哪些与工作不太相关的问题 ...

  9. Docker常见报错解决方法记录

    [问题一]OCI runtime exec failed......executable file not found in $PATH": unknown [root@localhost ...

  10. PHP路径指定web路径的方法

    PHP路径指定web路径的方法直接在/前面加.就是代表web路径了 不是按照文件路径来算了 <pre>./Public/uploads/suolutu/' . $suijishu . '_ ...