2014-03-26 35 views
1

我正在研究一個代碼,它在其構造函數中接受一個Map,並將其分配給一個實例變量。任何使用這個實例變量map的方法都是同步的。如何同步傳遞給多個地方的實例?

class Foo { 

    Map<Int, Int> map; 

    Foo (Map<Int, Int> map) { 
     this.map = map; 
    } 

    synchornized void put(int x, int y) { 
     map.put(x, y); 
    } 

} 

但是 - 客戶端創建了Foo的多個實例,並將相同的地圖實例傳遞給它們中的每一個。

Map<Int, Int> map = new HashMap<Int, Int>(); 
new Foo(map); 
new Foo(map); 

如何在不同情況下使用時,不會map得到同步? 使問題更廣泛,如何同步全局實例類?

回答

0

您可以使用ConcurrentHashMap - 此類型的Map是線程安全的。

或者,對類進行同步,以便該類的任何實例共享同一個鎖。

或者,在地圖上同步。

+0

1.使用ConcurrentHashMap不必解決問題:只有當「複雜操作」是ConcurrentMap的API的一部分時,ConcurrentMap纔有用。如果開發人員需要對Map執行多個操作並且希望它是原子操作,並且這種操作不是由ConcurrentMap提供的,那麼它不會提供幫助。即使使用ConcurrentHashMap,數據仍然不一致。 2.在課堂上同步是不合理的,因爲從概念上講,MAP是需要工作的資源並因此獲得鎖定。 –

+0

如果問題更復雜並且在給定的「交易」中使用多個地圖操作,那麼你是對的。但考慮到這個問題,這三個選項是有效的。 – Jason

+0

問題實際上是「讓問題更廣泛,如何同步全局實例類?」 –

1

您應該同步map,而不是foo實例本身。

void put(int x, int y) { 
    synchronized (map) { 
     map.put(x, y); 
    } 
} 

編輯:

的關鍵點在這裏,你需要了解同步的概念,並確定是共享的「資源」,並需同步。在你的情況下,共享資源是Map,而不是Foo實例本身。正在同時訪問的Map可能會導致問題,因此您應該同步的是共享資源 - Map本身。

還有一些建議使用ConcurrentHashMap的其他答案。 ConcurrentHashMap可以在某些情況下提供幫助,但它不會涵蓋同步塊的可能性(或使用某種其他類型的鎖定機制)

ConcurrentMap解決了爲過去需要提供一些「複雜」操作的問題多次訪問,因此需要對Map進行顯式鎖定。 ConcurrentHashMap通過進行線程安全無鎖讀取等更進一步。

但是,它沒有必要解決問題。

假設我需要在Map上執行的操作不是所提供的操作:

synchronized(map) { 
    if (map.contains("key1")) { 
     map.put("key3", val1); 
    } else if (map.contains("key2")) { 
     map.put("key3", val); 
    } 
} 

,那麼我就需要明確的鎖定(在這種情況下,ConcurrentHashMap的可能會更糟,因爲它不允許我進行顯式同步)

+0

是的,除了OP沒有詢問複雜的操作。 – Jason

+0

OP沒有告訴我們他在做什麼(這段代碼顯然不是真實的)。我想說的是,爲了正確執行同步,他應該瞭解的正確概念。也許可能會發現他只需要一個線程安全的映射(一個同步映射或併發散列映射),但這應該是他正確理解的決定,而不是盲目地將併發散列映射作爲解決方案危險 –

+0

我同意。在地圖上而不是在您自己的實例上同步。但是,這會帶來其他問題,並且您不能確定別人不會在鎖定之外操作地圖。基本上 - 共享需要同步的瞬態數據本質上是不安全的。 – Bex

0

使用ConcurrentHashMap而不是HashMap

哈希表支持檢索的完全併發和可調整的 預期的更新併發性。該類遵循與Hashtable相同的功能 規範,並且包括與Hashtable的每種方法對應的方法 的版本。但是,即使所有 操作都是線程安全的,但檢索操作不會導致鎖定,並且也不支持以防止所有訪問的方式鎖定整個表的 。這個類與 Hashtable在依賴其線程安全性的程序中完全可互操作,但不依賴其 同步細節。

+0

任何投票原因? –

+0

1.因爲併發哈希映射不需要能夠替換同步範圍。 2。OP實際上是要求更一般的同步策略 –

+0

您無法控制此類的用戶將要提交的映射。在這種情況下,你將不得不面對它 - 這將不僅僅是麻煩一點。 – Bex

1

我想補充以前的答案。

你所描述的模式的問題,沒有進一步的上下文,是你不知道什麼是實際發送或用於課外。你唯一知道的是它實現了Map。

如果你需要保護(同步)輸入到這張地圖,那麼這個模式就會被破壞,因爲調用者,首先將地圖提交給你的人也可能已經把它提交給了其他類,或者可能以其他方式操縱它,而沒有鎖定。

確保您正在操作的地圖得到妥善保護的唯一方法就是您自己擁有該地圖並確保您不會將引用泄露給其他人。

這意味着你應該將複製當你第一次得到它的時候將提交的地圖複製到私人地圖中。這樣,沒有人知道,沒有人可以操縱它。

class Foo { 
    private final Map<Integer, Integer> map; 

    Foo(Map<Integer, Integer> map) { 
    this.map = new HashMap<Integer, Integer>(map); 
    } 
} 

然後,你還可以利用現代的風格鎖定Java中:

class Foo { 
    private final Map<Integer, Integer> map; 
    private final ReentrantReadWriteLock mapLock = new ReentrantReadWriteLock(); 

    Foo(Map<Integer, Integer> map) { 
    this.map = new HashMap<Integer, Integer>(map); 
    } 

    public Integer put(Integer k, Integer v) { 
    try { 
     mapLock.writeLock().lock(); 
     return map.put(k, v); 
    } finally { 
     mapLock.writeLock().unlock(); 
    } 
    } 

    public Integer get(Integer k) { 
    try { 
     mapLock.readLock().lock(); 
     return map.get(k); 
    } finally { 
     mapLock.readLock().unlock(); 
    } 
    } 
} 

是安全的 - 複製您的輸入,不要泄露你參考!

相關問題