正确HashMap的同步

问题描述:

比方说,我有一个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

在地图本身上进行同步本质上是由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

所有对地图的调用都需要同步,而Collections.synchronizedMap()则为您提供了。

但是,复合逻辑也有一个方面。如果您需要复合逻辑的完整性,则单个呼叫的同步是不够的。例如,请考虑下面的代码:

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

虽然个别电话(得到()和put())是同步的,你的逻辑不会对并发访问安全。

另一个有趣的例子是当你迭代。为了保证迭代的安全性,您需要同步迭代的整个持续时间,否则将得到ConcurrentModificationExceptions。