1

I have a block of Java code that looks something like this that I'm trying to parallelize:

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. Accessing with other keys should be allowed. How could I achieve this?

1
  • Accessing with other keys isn't all that safe either. Adding a key/value (even a different one) involves changing internal stuff in the collection, and in extreme cases could cause the whole map to be resized while someone's trying to retrieve stuff from it. That could cause problems. e.g: lightbody.net/blog/2005/07/hashmapget_can_cause_an_infini.html
    – cHao
    Commented Apr 13, 2011 at 22:54

7 Answers 7

5

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. The basic idea is to use ConcurrentHashMap.putIfAbsent instead of the normal put.

2
  • 1
    Double-checked locking isn't unsafe; it can be made to work correctly from Java 5 onward. Whether it's worth doing is debatable, though, and it is not clear how it would be applied to this problem.
    – erickson
    Commented Apr 14, 2011 at 14:19
  • The link to the article is broken.
    – Gili
    Commented Jun 30, 2022 at 2:42
2

You cannot parallelize updates to HashMap because update can trigger resize of the underlying array including recalculation of all keys.

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." according to javadoc.

1

I wouldn't use HashMap if you need to be concerned about threading issues. Make use of the Java 5 concurrent package and look into ConcurrentHashMap.

1

You just described the use case for the Guava computing map. 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.

You probably don't actually want your value having a mutable update method on it, but that's another discussion.

0
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. You can see it on the JavaDoc.

1
  • This doesn't work if there are multiple functions. Variable-specific synchronization is better here. Commented Apr 13, 2011 at 22:51
0

Look into Concurrent 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.

0

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.