=========================分割線1=================================
版權聲明 :轉載時請以超鏈接形式標明文章原始出處和作者信息及本聲明
http://aleung.blogbus.com/logs/32090434.html
在Java內存模型中,有main memory,每個線程也有自己的memory (例如寄存器)。爲了性能,一個線程會在自己的memory中保持要訪問的變量的副本。這樣就會出現同一個變量在某個瞬間,在一個線程的memory中的值可能與另外一個線程memory中的值,或者main memory中的值不一致的情況。
一個變量聲明爲volatile,就意味着這個變量是隨時會被其他線程修改的,因此不能將它cache在線程memory中。以下例子展現了volatile的作用:
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代碼:
而 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就不是原子級別的:
n = n + 1 ;
n ++ ;
如果要想使這種情況變成原子操作,需要使用synchronized關鍵字,如上的代碼可以改成如下的形式: