volatile基礎
msdn的解釋:http://msdn.microsoft.com/zh-cn/library/x13ttww7.aspx
volatile 關鍵字指示一個字段可以由多個同時執行的線程修改。聲明爲 volatile 的字段不受編譯器優化(假定由單個線程訪問)的限制。這樣可以確保該字段在任何時間呈現的都是最新的值。
volatile 修飾符通常用於由多個線程訪問但不使用 lock 語句對訪問進行序列化的字段。有關在多線程方案中使用 volatile 的示例,請參見如何:創建和終止線程(C# 編程指南)。
volatile 關鍵字可應用於以下類型的字段:
1.引用類型
2.指針類型(在不安全的上下文中)
請注意,雖然指針本身可以是可變的,但是它指向的對象不能是可變的。換句話說,您無法聲明“指向可變對象的指針”。
3.整型
如 sbyte、byte、short、ushort、int、uint、char、float 和 bool。
4.枚舉類型
具有整數基類型的枚舉類型
5.已知爲引用類型的泛型類型參數。
6.IntPtr 和 UIntPtr。
PS:可變關鍵字僅可應用於類或結構字段。不能將局部變量聲明爲 volatile。
Terrylee的代碼
using System;
namespace SigletonPattern.Sigleton
{
/// <summary>
/// 功?能?:?在?C#用?雙?重?鎖?定?實?現?單?件?模?式?
/// 編?寫?:?Terrylee
/// 日?期?:?年?月?日?
/// </summary>
public class DoubLockSigleton
{
private static volatile DoubLockSigleton instance;
/// <summary>
/// 輔?助?鎖?對?象?,?本?身?沒?有?意?義?
/// </summary>
private static object syncRoot = new Object();
/// <summary>
/// 構?造?方?法?改?爲?Private
/// </summary>
private DoubLockSigleton()
{
}
public static DoubLockSigleton Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new DoubLockSigleton();
}
}
return instance;
}
}
}
}
volatile與synchronized
恐怕比較一下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()方法做了如下幾步:
- 線程請求獲得監視this對象的對象鎖(假設未被鎖,否則線程等待直到鎖釋放)
- 線程內存的數據被消除,從“主”內存區域中讀入(Java虛擬機能優化此步。。。[後面的不知道怎麼表達,汗])
- 代碼塊被執行
- 對於變量的任何改變現在可以安全地寫到“主”內存區域中(不過geti3()方法不會改變變量值)
- 線程釋放監視this對象的對象鎖
因此volatile只是在線程內存和“主”內存間同步某個變量的值,而synchronized通過鎖定和解鎖某個監視器同步所有變量的值。顯然synchronized要比volatile消耗更多資源。
更通俗的解釋:
Volatile 字面的意思時易變的,不穩定的。在C#中也差不多可以這樣理解。
編譯器在優化代碼時,可能會把經常用到的代碼存在Cache裏面,然後下一次調用就直接讀取Cache而不是內存,這樣就大大提高了效率。但是問題也隨之而來了。
在多線程程序中,如果把一個變量放入Cache後,又有其他線程改變了變量的值,那麼本線程是無法知道這個變化的。它可能會直接讀Cache裏的數據。但是很不幸,Cache裏的數據已經過期了,讀出來的是不合時宜的髒數據。這時就會出現bug。
用Volatile聲明變量可以解決這個問題。用Volatile聲明的變量就相當於告訴編譯器,我不要把這個變量寫Cache,因爲這個變量是可能發生改變的。