2011-12-16 46 views
3

我有一個關於Java中的volatile語句的問題。請看看這個構造例如:Java:足夠易於使類進行線程安全?

class Master { 
    // Foo is a class with thread-safe methods 
    public volatile Foo foo; 
} 

class Worker1 implements Runnable { 
    protected Master master 

    void run() { ... }; 
} 

class Worker2 implements Runnable { 
    protected Master master 

    void run() { ... }; 
} 

我們有堅持在同一時間運行的類碩士的一個對象的引用2個工作線程。在工作期間,他們都必須訪問master.foo的方法。在某些時候,master的Foo對象將被其中一個工作線程改變。現在我的問題:是否使用volatile使整個構造線程安全?我問這是因爲在Java tutorial from Oracle它說

不過,也有你可以指定動作是原子的:

  • 讀取和寫入原子參考變量[...]

原子動作不能交錯,所以可以使用它們而不用擔心線程干擾。

我只是想確保我正確理解這一點。

感謝提前:)

+0

你是什麼意思的'改變'? 1.將創建一個「Foo」的新實例,2.「Foo」的狀態將被修改? – home 2011-12-16 18:28:40

+0

對不清楚的聲明:這是關於改變參考,而不是對象本身。 – tbolender 2011-12-16 18:35:55

回答

9

引用的讀寫操作在Java中始終是原子的,因此不存在例如在某種半更新狀態下看到引用的危險。如果這就是「線程安全」的意思,那麼操作是線程安全的,有或沒有關鍵字。

但是volatile與原子性無關,它會影響線程是否可以在本地緩存寫入。 volatile將強制寫入回寫到主內存並對其他線程可見。如果這就是線程安全的意思,那麼你需要這個關鍵字。

volatile本身不影響foo上的方法調用是否排除其他線程。如果這是你的意思,你想使用​​,並在不同的地方。

1

如果你唯一要做的事情是設置這個變量,比對,揮發性就夠了。正如在評論中指出的那樣,你需要volatile來使更改可見(但是,如果沒有volatile,你將不會有任何損壞的更新,就像你可能擁有long或double一樣,見下文)。

默認情況下,讀寫參考變量(引用對象)是原子的,就像讀寫大多數基本類型(除了long和double)一樣。

由於volatile,您可以確保對long和double類型變量的讀寫操作也是原子操作,在這種情況下您不需要該操作。

所以,是的,在這個例子中,這就夠了。但是,如果你在發送更復雜的信息規劃,可以考慮使用工具從java.util.concurrent中。*

+0

沒有不一定沒有揮發性(一個簡單的setter/getter不會是線程安全的/可見的) – 2011-12-16 18:27:11

+2

如果沒有volatile關鍵字,這個改變可能對任何長時間的其他線程都是不可見的。雖然這不會導致不一致,但它可能不是所需的行爲。 – 2011-12-16 18:28:14

4

volatile確保線程讀取變量會看到另一個線程之前在這個變量設置新值。它不會看到存儲在處理器緩存中的陳舊值。

它是否使整個程序線程安全取決於程序的功能,它如何使用變量以及如何設計Foo類。

3

不穩定是不夠的。

相信本實施例顯示一種情況,其中易失性是不夠的:

共享對象:

public class Blammo 
{ 
    public String kapow; 
} 

代碼在線程1:

Blammo blammo = new Blammo(); 
blammo.kapow = "zowie"; 
if ((blammo.kapow != null) && (blammo.kapow.isEmpty())) 
{ 
    ... 
} 

代碼在線程2:

blammo.kapow = null; 

線程1正在執行並執行(blammo.kapow!= null)。
線程1掉換和線程2交換。
線程2執行(blammo.kapow = null)。
線程2掉換和線程1交換。
線程1現在執行(blammo.kapow.isEmpty())並拋出空指針異常。