前些日子在看些多线程方面的资料,当我看到对volatile这个关键字的解释的时候,让我出现了一些困惑!
在某些书籍中说
然而一篇IBM技术论坛上的文章似乎又有不同的观点(http://www.ibm.com/developerworks/cn/java/j-jtp06197.html)
- volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。
volatile变量具有 synchronized 的可见性特性,但是不具备原子特性。
两段文字粗略一看,似乎搞得人晕头转向了,更加难以理解volatile的特性了!
但是请注意第一段文字中被标红的一段"简单的赋值与返回操作的"原子性",这里似乎说明这种原子性不可靠!因为long和double类型的长度都是64位的,由于jvm的原因可能造成撕扯现象.这里的简单原子性应该是保证变量不会被撕扯得面目全非吧!
有人对volatile做如下整理(http://hi.baidu.com/lifa868/blog/item/22bc7718926ad772dbb4bd0e.html):
Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。
这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。
而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。
由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
后来在一篇对C#中volatile相关文章中看到这样一句话(http://www.cnblogs.com/lucifer1982/archive/2008/03/23/1116981.html):
还有这样一种理解,volatile在增量操作时已经失去了原子性,如i++其实是i=i+1,这种情况下我们就认为原子性将丢失。而在i=m的情况下是可以保证原子性的!
这样一来是不是可以做出以下理解:
java中有主内存和工作内存之分,当变量变申明volatile之后.
1.所有的读写操作都直接从主内存进行.
2.每次对变量的修改强行回写到主内存中,供其他线程共享!
最后想做一个假设,以说明自己对上诉观点的理解:
- static volatile int i=0;
- static void add(){
- i++;
- }
- .......
static volatile int i=0;
static void add(){
i++;
}
.......
在多线程并发访问add()方法的时候,T1线程和T2线程同时进入add(),由于{i++}=={i=i+1}其实是先从主内存中读取i,而后对i进行增量修改,最后回写主内存。那就有可能出现这样的情况:在T1进入后读取的i=9,T2同时也读取的也是9,T1线程操作后i=10然后回写主内存,T2也同时完成操作i=10也回写主内存,那T2的回写值是不是就覆盖了T1的回写值,这也就是上述中所提及的“如果多线程试图进行增量操作,很可能丢失其更新值”。
由于对jvm内存设计不是非常熟悉,不清楚实际jvm中对volatile是否在内存也是这样操作的,希望大家能提供更多资料以便相互交流,探讨!