简体   繁体   English

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

[英]How to safely modify values in Java HashMaps concurrently?

I have a block of Java code that looks something like this that I'm trying to parallelize: 我有一块Java代码,看起来像这样,我正在尝试并行化:

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

I want to block any other thread from accessing the map with that particular key until after value.update() is called even if key is not in the key set . 我想阻止其他任何线程使用该特定键访问映射,直到调用value.update()之后, 即使键不在键集中也是如此 Accessing with other keys should be allowed. 应允许使用其他键进行访问。 How could I achieve this? 我怎样才能做到这一点?

Short answer is there's no safe way to do this without synchronizing the entire block. 简短的答案是,没有同步整个块就没有安全的方法。 You could use java.util.concurrent.ConcurrentHashMap though, see this article for more details. 不过,您可以使用java.util.concurrent.ConcurrentHashMap有关更多详细信息 ,请参见本文。 The basic idea is to use ConcurrentHashMap.putIfAbsent instead of the normal put . 基本思想是使用ConcurrentHashMap.putIfAbsent而不是普通put

You cannot parallelize updates to HashMap because update can trigger resize of the underlying array including recalculation of all keys. 您无法将更新并行化到HashMap,因为更新会触发基础数组的大小调整,包括重新计算所有键。

Use other collection, for example java.util.concurrent.ConcurrentHashMap which is a "A hash table supporting full concurrency of retrievals and adjustable expected concurrency for updates." 使用其他集合,例如java.util.concurrent.ConcurrentHashMap,它是一个“哈希表,支持检索的完全并发和可调整的预期并发更新。” according to javadoc. 根据javadoc。

I wouldn't use HashMap if you need to be concerned about threading issues. 如果您需要关注线程问题,则不会使用HashMap。 Make use of the Java 5 concurrent package and look into ConcurrentHashMap. 利用Java 5并发包并查看ConcurrentHashMap。

You just described the use case for the Guava computing map. 您刚刚描述了Guava计算图的用例。 You create it with: 使用以下方法创建它:

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

and use it: 并使用它:

Value v = map.get(key);

This guarantees only one thread will call update() and other threads will block and wait until the method completes. 这样可以确保只有一个线程将调用update()而其他线程将阻塞并等待该方法完成。

You probably don't actually want your value having a mutable update method on it, but that's another discussion. 您可能实际上并不希望您的值具有可变的更新方法,但这是另一种讨论。

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

You can learn more about synchronized methods here: Synchronized Methods 您可以在此处了解有关同步方法的更多信息: 同步方法

You might also want to investigate the ConcurrentHashMap class, which might suit your purposes. 您可能还想研究可能适合您目的的ConcurrentHashMap类。 You can see it on the JavaDoc. 您可以在JavaDoc上看到它。

Look into Concurrent HashMap . 查看并发HashMap It has excellent performance even for single-threaded applications. 即使对于单线程应用程序,它也具有出色的性能。 It allows concurrent modification of Map from various threads without any need of blocking them. 它允许从各个线程并发修改Map,而无需阻止它们。

One possibility is to manage multiple locks. 一种可能性是管理多个锁。 So you can keep an array of locks that is retrieved based on the key's hash code. 因此,您可以保留一个基于键的哈希码检索的锁数组。 This should give you better through-put then synchronizing the whole method. 这样可以为您提供更好的吞吐量,然后再同步整个方法。 You can size the array based on the number of thread that you believe will be accessing the code. 您可以根据您认为将要访问代码的线程数来确定数组的大小。

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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM