2012-03-01 57 views
1

比方說,我有一個HashMap聲明如下:正確HashMap的同步

@GuardedBy("pendingRequests") 
private final Map<UInt32, PendingRequest> pendingRequests = new HashMap<UInt32, PendingRequest>(); 

訪問地圖是多線程的,並且所有的訪問是通過同步在地圖上的最後實例,如守衛:

synchronized (pendingRequests) { 
    pendingRequests.put(reqId, request); 
} 

這夠了嗎?地圖是否應該使用Collections.synchronizedMap()創建?我應該鎖定一個專用鎖對象而不是地圖實例嗎?或者兩者兼得?

需要在地圖上多次調用必須爲原子的幾個區域外部同步(除了可能使用Collections.synchronizedMap()之外)。

+0

@KevinHerron爲什麼不使用['ConcurrentHashMap'](http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ConcurrentHashMap.html)? – 2012-03-01 20:27:13

回答

6

在地圖本身上進行同步本質上是由Collection.synchronizedMap()返回的Map所做的。對於你的情況,這是一個合理的方法,除了個人偏好(或者如果你希望有更細緻的控制並使用ReentrantReadWriteLock來允許同時讀取地圖),建議使用單獨的鎖對象並沒有太多的建議。

E.g.

private Map<Integer,Object> myMap; 
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); 

public void myReadMethod() 
{ 
    rwl.readLock().lock(); 
    try 
    { 

    myMap.get(...); 
    ... 
    } finally 
    { 
    rwl.readLock().unlock(); 
    } 
} 

public void myWriteMethod() 
{ 
    // may want/need to call rwl.readLock().unlock() here, 
    // since if you are holding the readLock here already then 
    // you cannot get the writeLock (so be careful on how your 
    // methods lock/unlock and call each other). 
    rwl.writeLock().lock(); 
    try 
    { 
    myMap.put(key1,item1); 
    myMap.put(key2,item2); 
    } finally 
    { 
    rwl.writeLock().unlock(); 
    } 
} 
+1

+1。好答案。我只補充說@Kevin需要確保'get()'和其他對地圖的調用也是'synchronized'。 – Gray 2012-03-01 19:29:58

+0

好的,謝謝。 @格雷,我確保所有的呼叫都是同步的。 – 2012-03-01 19:58:06

1

所有對地圖的調用都需要同步,而Collections.synchronizedMap()則爲您提供了。

但是,複合邏輯也有一個方面。如果您需要複合邏輯的完整性,則單個呼叫的同步是不夠的。例如,請考慮下面的代碼:

Object value = yourMap.get(key); // synchronized 
if (value == null) { 
    // do more action 
    yourMap.put(key, newValue); // synchronized 
} 

雖然個別電話(得到()和put())是同步的,你的邏輯不會對併發訪問安全。

另一個有趣的例子是當你迭代。爲了保證迭代的安全性,您需要同步迭代的整個持續時間,否則將得到ConcurrentModificationExceptions。