我在Java中有一个大的map = ConcurrentHashMap()而Key,Value是某种Object结构。假设该地图的密钥集是 keySet 。
现在我有一个计算程序。我的问题是如何通过不使用整个地图锁来获得更好的性能。是否有任何选项,如使用按键锁或使用其他类型的数据结构?
考虑到这是一张大地图,使用每键锁定可能不是一种可接受的方法。
multiThread(): for(0 to N): K = subset(keySet, m) where m is much smaller than keySet.size lock(map) for(key in K): result = func1(map.get(key), result) for(key in K): map.put(key, func2(map.get(key), result)) releaseLock(map)
答案 0 :(得分:2)
在java 8+ ConcurrentHashMap中作为compute() function,允许您对单个键执行原子读 - 修改 - 写操作,因此您可以执行以下操作:
map.compute(key, () -> {
//call func2 to compute new value and return it
});
但是,如果您想要对整个键集进行原子读取 - 修改 - 写入(因此,首先迭代您的键集来计算结果,然后使用该预先计算的结果更改所有这些键)然后&# 39;在ConcurrentHashMap中没有任何工具来提供锁定。
但是,您可以使用番石榴Striped lock,如下所示:Striped<Lock> arrayOfLocks = Striped.lock(20);
// ...later on...
K = subset(keySet, m);
Iterable<Lock> toObtain = arrayOfLocks.bulkGet(K);
for (Lock l : toObtain) { lock it }
try {
//do your modifications - your holding the stripe locks for all the keys
} finally {
for (Lock l : toObtain) { unlock it }
}
锁定条带化是将锁分配给数据结构的不同区域的概念 - 这里是通过键的hashCode完成的。
你需要非常小心地选择锁定阵列的大小以平衡太少的条纹(你将锁定整个地图并且比单个全局锁更慢)和太多条纹(其中根据K的大小,你会抓住很多锁。
Striped负责以相同的顺序返回锁定以锁定同一组钥匙,以避免餐饮哲学家的问题。