2016-09-15 46 views
0
final class NameVolatile { 
    @Nullable private volatile String name; 

    void setName(String name) { 
    this.name = name 
    } 

    void run() { 
    String name = name; 
    if (name != null) { 
     print(name); 
    } 
    } 
} 

final class NameSynchronized { 
    private final Object guard = new Object(); 
    @Nullable private String name; 

    void setName(String name) { 
    synchronized(guard) { 
     this.name = name 
    } 
    } 

    void run() { 
    synchronized(guard) { 
     if (name != null) { 
     print(name); 
     } 
    } 
    } 
} 

以上是兩種方式來完成幾乎相同的事情的一個例子,但我不知道什麼時候更喜歡一個或另一個。對於可變引用字段中的不可變類型,請在本地使用volatile和cache或同步?

什麼情況下哪一個比另一個更有用?

我相信這個問題不同於Volatile or synchronized for primitive type?,因爲那裏的問題和答案沒有提到有一個本地緩存變量的做法。

+0

可能重複的[原始類型的易失性或同步?](http://stackoverflow.com/questions/1779258/volatile-or-synchronized-for-primitive-type) – 4castle

+1

我建議改爲使用['AtomicReference '](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html) – 4castle

+0

與沒有本地緩存​​的易失性存儲器相比,它們有沒有同樣的缺點?它可以在空檢查和打印語句之間設置。 –

回答

1

在本地使用volatile和cache還是同步?

我想你錯了關於volatile關鍵字的作用。對於所有現代OS處理器,都有一個本地每處理器內存緩存。 volatile關鍵字不啓用本地緩存 - 您可以將「免費」作爲現代計算機硬件的一部分。這種緩存是多線程程序性能增加的重要部分。

volatile關鍵字可確保在讀取字段時讀取內存屏障,確保在處理器高速緩存中更新所有更新的中央內存塊。寫入volatile字段意味着寫入內存屏障已通過,以確保將本地高速緩存更新寫入中央內存。此行爲與您在​​塊中跨越的內存障礙完全相同,爲。當您輸入​​塊時,將讀取內存屏障,並且當您離開寫入時將被劃過。

​​和volatile之間的最大區別是您支付與​​鎖定。​​是必要當有多個操作同時發生時,您需要將操作包裝在互斥鎖中。如果你只是想保持你的name字段與主內存正確更新然後volatile是要走的路。

另一個選項是AtomicReference,其中包含private volatile Object字段並提供原子方法,如compareAndSet(...)。即使你沒有使用特殊的方法,許多程序員也覺得這是封裝需要進行內存同步的字段的好方法。

最後,volatile和​​也提供了「發生之前」保證,它控制指令重新排序,這對確保程序中正確的操作順序很重要。

在代碼方面,你永遠不應該這樣做:

synchronized(guard) { 
    if (name != null) { 
     print(name); 
    } 
} 

你不想做昂貴的IO一個​​塊內。它應該是這樣的:

// grab a copy of the name so we can do the print outside of the sync block 
String nameCopy; 
synchronized(guard) { 
    nameCopy = name; 
} 
if (nameCopy != null) { 
    print(nameCopy); 
} 

隨着volatile,你想要做一個volatile場查找所以像以下建議:

void run() { 
    // only do one access to the expensive `volatile` field 
    String nameCopy = name; 
    if (nameCopy != null) { 
     print(nameCopy); 
    } 
} 

最後,從評論,volatile是顯著比更貴一個正常的操作(可以使用高速緩存的內存)但volatile明顯是較少比​​塊昂貴,它必須測試並更新鎖定狀態,塊會跨越影響所有緩存內存的內存障礙。我的信封測試表明,這表現出這種性能差異。

+0

很酷,是的,我瞭解你指出的基本知識;我只是對上面列出的兩種風格感興趣。這是關於該區塊昂貴的操作的一個非常好的觀點。我只是以此爲例,但感謝提醒。你的第二個片段是我的第二個解決方案,看起來像。所以,我認爲(除了block內有'print')我的解決方案是相當的,那麼,對吧? –

+0

等同於內存同步和指令排序。在性能方面完全不等同@EricCochran。 – Gray

+0

性能差異僅僅是因爲印刷中暗示的I/O,但是,對嗎?其他性能差異? –