正确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()
之外)。
在地图本身上进行同步本质上是由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。好答案。我只补充说@Kevin需要确保'get()'和其他对地图的调用也是'synchronized'。 – Gray 2012-03-01 19:29:58
好的,谢谢。 @格雷,我确保所有的呼叫都是同步的。 – 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。
@KevinHerron为什么不使用['ConcurrentHashMap'](http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ConcurrentHashMap.html)? – 2012-03-01 20:27:13