1. 程式人生 > >同步和Java記憶體模型(五)Volatile

同步和Java記憶體模型(五)Volatile

作者:Doug lea 譯者:杜建雄校對者:方騰飛

Volatile

從原子性,可見性和有序性的角度分析,宣告為volatile欄位的作用相當於一個類通過get/set同步方法保護普通欄位,如下:

final class VFloat {
    private float value;

    final synchronized void set(float f) { value = f; }
    final synchronized float get()       { return value; }
}

與使用synchronized相比,宣告一個volatile欄位的區別在於沒有涉及到鎖操作。但特別的是對volatile欄位進行“++”這樣的讀寫操作不會被當做原子操作執行。

另外,有序性和可見性僅對volatile欄位進行一次讀取或更新操作起作用。宣告一個引用變數為volatile,不能保證通過該引用變數訪問到的非volatile變數的可見性。同理,宣告一個數組變數為volatile不能確保陣列內元素的可見性。volatile的特性不能在陣列內傳遞,因為數組裡的元素不能被宣告為volatile。

由於沒有涉及到鎖操作,宣告volatile欄位很可能比使用同步的開銷更低,至少不會更高。但如果在方法內頻繁訪問volatile欄位,很可能導致更低的效能,這時還不如鎖住整個方法。

如果你不需要鎖,把欄位宣告為volatile是不錯的選擇,但仍需要確保多執行緒對該欄位的正確訪問。可以使用volatile的情況包括:

  • 該欄位不遵循其他欄位的不變式。
  • 對欄位的寫操作不依賴於當前值。
  • 沒有執行緒違反預期的語義寫入非法值。
  • 讀取操作不依賴於其它非volatile欄位的值。

當只有一個執行緒可以修改欄位的值,其它執行緒可以隨時讀取,那麼把欄位宣告為volatile是合理的。例如,一個名叫Thermometer(中文:體溫計)的類,可以宣告temperature欄位為volatile。正如在3.4.2節所討論,一個volatile欄位很適合作為完成某些工作的標誌。另一個例子在4.4節有描述,通過使用輕量級的執行框架使某些同步工作自動化,但是仍需把結果欄位宣告為volatile,使其對各個任務都是可見的。

原文

Synchronization and the Java Memory Model

Volatile

In terms of atomicity, visibility, and ordering, declaring a field as volatile is nearly identical in effect to using a little fully synchronized class protecting only that field via get/set methods, as in:

final class VFloat {
  private float value;

  final synchronized void  set(float f) { value = f; }
  final synchronized float get()        { return value; }
}

Declaring a field as volatile differs only in that no locking is involved. In particular, composite read/write operations such as the “++” operation on volatile variables are not performed atomically.

Also, ordering and visibility effects surround only the single access or update to the volatile field itself. Declaring a reference field as volatile does not ensure visibility of non-volatile fields that are accessed via this reference. Similarly, declaring an array field as volatile does not ensure visibility of its elements. Volatility cannot be manually propagated for arrays because array elements themselves cannot be declared as volatile.

Because no locking is involved, declaring fields as volatile is likely to be cheaper than using synchronization, or at least no more expensive. However, if volatile fields are accessed frequently inside methods, their use is likely to lead to slower performance than would locking the entire methods.

Declaring fields as volatile can be useful when you do not need locking for any other reason, yet values must be accurately accessible across multiple threads. This may occur when:

  • The field need not obey any invariants with respect to others.
  • Writes to the field do not depend on its current value.
  • No thread ever writes an illegal value with respect to intended semantics.
  • The actions of readers do not depend on values of other non-volatile fields.

Using volatile fields can make sense when it is somehow known that only one thread can change a field, but many other threads are allowed to read it at any time. For example, a Thermometer class might declare its temperature field as volatile. As discussed in 3.4.2, a volatile can be useful as a completion flag. Additional examples are illustrated in 4.4, where the use of lightweight executable frameworks automates some aspects of synchronization, but volatile declarations are needed to ensure that result field values are visible across tasks.


杜 建雄

2012年畢業,先後在UC北京分部和廣州UC工作。主要參與後端程式的java開發以及php web開發。對java多執行緒程式設計感興趣,期望用程式設計技能做些與自身愛好相結合的事情。stay hungry,stay foolish(像外行一樣思考,像專家一樣實踐)。2015年從心出發