如何同时安全地修改Java HashMaps中的值?

时间:2011-04-13 22:42:26

标签: java concurrency hashmap

我有一块看起来像我正在尝试并行化的Java代码:

value = map.get(key);
if (value == null) {
    value = new Value();
    map.put(key,value);
}
value.update();

我想阻止任何其他线程使用该特定键访问地图,直到value.update()被调用,即使密钥不在密钥集中。应允许使用其他密钥访问。我怎么能做到这一点?

7 个答案:

答案 0 :(得分:5)

简短的回答是,如果不同步整个块,就没有安全的方法。您可以使用java.util.concurrent.ConcurrentHashMap,请参阅this article for more details.基本提示是使用ConcurrentHashMap.putIfAbsent代替普通put

答案 1 :(得分:2)

您无法并行化对HashMap的更新,因为更新可以触发基础数组的大小调整,包括重新计算所有键。

使用其他集合,例如java.util.concurrent.ConcurrentHashMap,它是“支持检索的完全并发和可更新的预期并发性的哈希表”。根据javadoc。

答案 2 :(得分:1)

如果您需要关注线程问题,我不会使用HashMap。使用Java 5并发包并查看ConcurrentHashMap。

答案 3 :(得分:1)

您刚刚描述了Guava计算地图的用例。您可以使用以下命令创建它:

Map<Key, Value> map = new MapMaker().makeComputingMap(new Function<Key, Value>() {
  public Value apply(Key key) {
    return new Value().update();
  }
));

并使用它:

Value v = map.get(key);

这保证只有一个线程会调用update(),其他线程将阻塞并等待该方法完成。

您可能实际上并不希望您的值具有可变更新方法,但这是另一个讨论。

答案 4 :(得分:0)

private void synchronized functionname() {
    value = map.get(key);
    if (value == null) {
        value = new Value();
        map.put(key,value);
    }
    value.update();
}

您可以在此处详细了解同步方法:Synchronized Methods

您可能还想调查ConcurrentHashMap类,这可能适合您的目的。您可以在JavaDoc上看到它。

答案 5 :(得分:0)

查看Concurrent HashMap。即使对于单线程应用程序,它也具有出色的性能。它允许从各种线程并发地修改Map,而无需阻止它们。

答案 6 :(得分:0)

一种可能性是管理多个锁。因此,您可以保留基于密钥哈希码检索的锁数组。这应该给你更好的吞吐量,然后同步整个方法。您可以根据您认为将访问代码的线程数来调整数组的大小。

private static final int NUM_LOCKS = 16;
Object [] lockArray = new Object[NUM_LOCKS];
...
// Load array with Objects or Reentrant Locks

...

Object keyLock = lockArray[key.hashcode % NUM_LOCKS];
synchronize(keyLock){
  value = map.get(key);
  if (value == null) {
    value = new Value();
    map.put(key,value);
  }
  value.update();
}