多线程 说说volatile关键字

来源:互联网 发布:画设计图用什么软件 编辑:程序博客网 时间:2024/06/08 11:58
Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。 
这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。 
而volatile关键字就是提示JVM:(这就是volatile的原理)对于这个成员变量不能保存它的私有拷贝(在线程栈),而应直接与共享成员变量(在主存中)交互。 
(打个广告,有兴趣的童鞋可以去看看http://hellosure.iteye.com/blog/1121157中有JMM的详细介绍) 
使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。 

使用volatile的目的 
Java语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量。这两种机制的提出都是为了实现代码线程的安全性。其中 Volatile 变量的同步性较差(但有时它更简单并且开销更低,这是volatile的优点),而且其使用也更容易出错(这是缺点)。 
首先考虑一个问题,为什么变量需要volatile来修饰呢? 
要搞清楚这个问题,首先应该明白计算机内部都做什么了。比如做了一个i++操作,计算机内部做了三次处理:读取-修改-写入。 
同样,对于一个long型数据,做了个赋值操作,在32系统下需要经过两步才能完成,先修改低32位,然后修改高32位。 
假想一下,当将以上的操作放到一个多线程环境下操作时候,有可能出现的问题,是这些步骤执行了一部分,而另外一个线程就已经引用了变量值,这样就导致了读取脏数据的问题。 
通过这个设想,就不难理解volatile关键字了。 
另外,volatile可以用在任何变量前面,但不能用于final变量前面,因为final型的变量是禁止修改的。也不存在线程安全的问题。 

volatile可以用更低的代价替代同步,那么为什么代价更低? 
同步的代价, 主要由其覆盖范围决定, 如果可以降低同步的覆盖范围, 则可以大幅提升程序性能. 
而volatile的覆盖范围仅仅变量级别的. 因此它的同步代价很低。 

volatile与synchronized比较? 
volatile 变量可以被看作是一种“程度较轻的synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。 
Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。 
什么意思呢?看看下面这个例子: 

比如volatile变量失效的一中情况:http://caoruntao.iteye.com/blog/658074 
声明为volatile的简单变量如果当前值由该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式n++;不是原子操作 
这同时也说明了为什么说volatile容易出错,因为volatile仅仅能保证变量可见性, 无法保证原子性。因此volatile运算有可能存在脏数据问题。 
虽然增量操作(n++)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。实现正确的操作需要使 x 的值在操作期间保持不变,而 volatile 变量无法实现这点。(然而,如果将值调整为只从单个线程写入,那么可以忽略第一个条件。) 
解决这个问题的方法:一是上文给出的synchronized同步方式,但是这显然违背了使用volatile的初衷。另一种是采用CAS非阻塞算法(打个广告在http://hellosure.iteye.com/blog/1126541介绍ConcurrentLinkedQueue中有详细介绍,有兴趣可以看看) 

正确使用volatile 的模式 
很多并发性专家事实上往往引导用户远离 volatile 变量,因为使用它们要比使用锁更加容易出错。然而,如果谨慎地遵循一些良好定义的模式,就能够在很多场合内安全地使用 volatile 变量。要始终牢记使用 volatile 的限制 —— 只有在状态真正独立于程序内其他内容时才能使用 volatile —— 这条规则能够避免将这些模式扩展到不安全的用例。 
具体的使用模式请参考http://www.ibm.com/developerworks/cn/java/j-jtp06197.html,这是篇很好的文章,建议仔细看看 
原创粉丝点击