1. 程式人生 > >Java 基礎系列之volatile變量(一)

Java 基礎系列之volatile變量(一)

釋放 pos clu 每次 修改 mil 可見 family 依賴

一、鎖
  兩種特性:互斥性(mutual exclusion)、可見性(visibility)、原子性(atomic)
  互斥性就是一次只有一個線程可以訪問該共享數據,可見性就是釋放鎖之前,對共享數據的修改,隨後獲取鎖的另一個線程是可見的,也就是說一個線程修改了共享變量的值,另一個線程訪問該共享變量的時候能立即得到最新修改的值。原子性就是多個變量或者某個變量的當前值和修改值之間存在某種一定約束。

二、volatile
  volatile變量具有可見性,但不具有原子性。這就是說線程能夠自動發現votatile變量的最新值。如果想讓volatile變量提供線程安全,這我們必須滿足該變量滿足下面2個特性。另一方面,volatile變量可以防止該變量操作的指令重排和優化。
  第一、對於volatile變量的寫操作不能依賴於該變量的當前值,比如volatile修飾變量x,而變量x操作:x++。如果多個線程操作x++,並不能達到預期的結果,原因在於x++這個操作是:首先cpu從內存中讀取變量x的值到cpu的一級cache中,接著cpu對cache中的值做修改,最後把cache中修改的值回寫到內存中。這一組操作需要以原子性的方式執行,才能保證線程安全,但是volatile本身不滿足原子性。
  第二、該volatile變量沒有在具有其他變量的不變式中,比如2個volatile變量start和end,而 start<=end這個語句在多線程中可能出現線程安全問題。
三、性能


  一般情況下使用volatile變量的同步機子的性能要優於鎖,就是說,在目前大多數的處理器架構上,volatile讀操作開銷非常低 —— 幾乎和非volatile讀操作一樣。而 volatile寫操作的開銷要比非volatile寫操作多很多,因為要保證可見性需要實現內存界定(Memory Fence),即便如此,volatile 的總開銷仍然要比鎖獲取低。
四、運用
  在使用volatile的時候,需要記住該變量的狀態獨立於程序其他內容時,才能使用volatile變量。如下是幾個運用:
  1、狀態標誌。比如volatile變量isInitialized是一個boolean類型,用於表示某種操作是否已經初始化完成。
  2、一次性安全發布
  3、獨立觀察
  4、開銷較低的讀-寫鎖策略
五、總結

  如果一個變量被volatile修飾,那麽cpu在讀取該變量的時候都不會從cpu本身的cache緩存中讀取,而是每次從內存中讀取,這樣保證了每個volatile變量的可見性。但是volatile變量不滿足原子性。更加說明一下x++的不是線程安全的。比如多個線程操作volatile變量x進行x++.其中x初始值是0,線程A和線程B都從內存中讀取x到各自的cache中,如cacheA(x=0),和cacheB(x=0),此時2個線程各自在自己的cache中操作變量x,這時線程A的cache值為cacheA(x=1),線程B的cache值為cacheB(x=1)。由於變量x是volatile類型,所以線程會把修改變量x的值立即回寫到內存中。比如線程A立即回寫,則變量x的值在內存中為1,而後線程B也回寫,則變量x的值為1。這跟預期的結果有差距的。

Java 基礎系列之volatile變量(一)