java的 volatile原來是這樣?

前些日子在看些多線程方面的資料,當我看到對volatile這個關鍵字的解釋的時候,讓我出現了一些困惑!

 

在某些書籍中說

寫道
'在Java中設置變量值的操作,除了long和double類型的變量外都是原子操作,也就是說,對於變量值的簡單讀寫操作沒有必要進行同步.當你定義long或double變量時,如果使用volatile關鍵字,就會獲得(簡單的賦值與返回操作的 )原子性'
 

 然而一篇IBM技術論壇上的文章似乎又有不同的觀點(http://www.ibm.com/developerworks/cn/java/j-jtp06197.html)

Volatile 變量具有 synchronized 的可見性特性,但是不具備原子特性。

 兩段文字粗略一看,似乎搞得人暈頭轉向了,更加難以理解volatile的特性了!

但是請注意第一段文字中被標紅的一段"簡單的賦值與返回操作的 原子性",這裏似乎說明這種原子性不可靠!因爲long和double類型的長度都是64位的,由於jvm的原因可能造成撕扯現象.這裏的簡單原子性應該是保證變量不會被撕扯得面目全非吧!

有人對Volatile做如下整理(http://hi.baidu.com/lifa868/blog/item/22bc7718926ad772dbb4bd0e.html):

寫道
Volatile修飾的成員變量在每次被線程訪問時,都強迫從共享內存中重讀該成員變量的值。而且,當成員變量發生變化時,強迫線程將變化值回寫到共享內存。這樣在任何時刻,兩個不同的線程總是看到某個成員變量的同一個值。

Java語言規範中指出:爲了獲得最佳速度,允許線程保存共享成員變量的私有拷貝,而且只當線程進入或者離開同步代碼塊時才與共享成員變量的原始值對比。

這樣當多個線程同時與某個對象交互時,就必須要注意到要讓線程及時的得到共享成員變量的變化。

而volatile關鍵字就是提示VM:對於這個成員變量不能保存它的私有拷貝,而應直接與共享成員變量交互。

使用建議:在兩個或者更多的線程訪問的成員變量上使用volatile。當要訪問的變量已在synchronized代碼塊中,或者爲常量時,不必使用。

由於使用volatile屏蔽掉了VM中必要的代碼優化,所以在效率上比較低,因此一定在必要時才使用此關鍵字。

 

後來在一篇對C#中volatile相關文章中看到這樣一句話(http://www.cnblogs.com/lucifer1982/archive/2008/03/23/1116981.html):

寫道
到這裏,我們已經知道volatile提供的同步機制還不足以能夠實現線程安全計數器。因爲計數器雖然簡單,卻是三種操作的組合,如果多線程試圖進行增量操作,很可能會丟失其更新值。

 

還有這樣一種理解,volatile在增量操作時已經失去了原子性,如i++其實是i=i+1,這種情況下我們就認爲原子性將丟失。而在i=m的情況下是可以保證原子性的!

這樣一來是不是可以做出以下理解:

   java中有主內存和工作內存之分,當變量變申明volatile之後.

1.所有的讀寫操作都直接從主內存進行.

2.每次對變量的修改強行回寫到主內存中,供其他線程共享!

最後想做一個假設,以說明自己對上訴觀點的理解:

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是否在內存也是這樣操作的,希望大家能提供更多資料以便相互交流,探討!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章