我有一个HashMap
并且想要单独同步每一行/条目以最大化并发性,因此这样许多线程可以同时访问HashMap
但没有两个或更多线程可以同时访问同一行/条目。
我在代码中执行了以下操作,但我不确定它是否正确:
/* Lock/synchronize the data to this key, (skey is a key of type String) */
synchronized (aHashMap.get(skey)) {
/* write the data (data is Integer) */
aHashMap.put(skey, data);
}
最佳答案
适当的解决方案很大程度上取决于您的具体问题。如果所有线程都可以更新 Map 中的任何条目,那么首先要尝试的是 ConcurrentHashMap :
在这种情况下,您描述的操作将替换为:
data = ... compute ...
aHashMap.replace(skey, data);
使用 ConcurrentHashMap 解决了数据竞争,但仍然存在一个问题。如果另一个线程同时更新相同的 key ,则其中一项计算将会丢失。如果您对此感到满意,那就太好了。否则,您可以:
do {
oldData = aHashMap.get(skey);
data = ... compute (maybe based on oldData) ...
boolean success = aHashMap.replace(skey, oldData, data);
} while(!success);
在这种情况下,仅当数据未更改时替换才会成功(并且替换是原子的)。如果失败,您可以将所有内容放入 do while 循环中重试,也许基于更新的值。
此外,请注意 map 获取和替换之间不要产生任何副作用。该计算应该只创建一个全新的“数据”对象。如果您更新“oldData”对象或其他一些共享数据,您将得到意想不到的结果。
如果确实有副作用,一种方法是像这样创建键级锁:
synchronized(skey) {
data = ... compute ...
aHashMap.replace(skey, data);
}
即使在这种情况下,仍然需要ConcurrentHashMap。此外,这不会阻止其他一些代码更新 map 中的该键。所有更新 key 的代码都需要锁定它。
此外,如果您更新“...compute...”中的 oldData 并且这些值在 map 中不唯一,则这将不是线程安全的。如果您确实想在那里更新 oldData,请用另一个同步覆盖它。
如果这确实有效并且您的内容与性能相符,那么就不用再犹豫了。
如果线程只更新值,而不更改键,那么您可以尝试将对转换为对象并使用与 Map 不同的东西。例如,您可以将对象集拆分为多个集合,然后将它们提供给线程。或者也许使用 ParallelArray。但我可能在这里离题了......:)
关于java - 数据行级别的并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7700303/