Java ConcurrentMap

Jakob Jenkov
Last update: 2024-11-28

java.util.concurrent.ConcurrentMap

The java.util.concurrent.ConcurrentMap interface represents a Java Map which is capable of handling concurrent access (puts and gets) to it.

The ConcurrentMap has a few extra atomic methods in addition to the methods it inherits from its superinterface, java.util.Map.

Java ConcurrentMap Tutorial Video

If you prefer video, I have a Java ConcurrentMap tutorial videre available here:

></a>



<a name=

ConcurrentMap Implementations

Since ConcurrentMap is an interface, you need to use one of its implementations in order to use it. The java.util.concurrent package contains the following implementations of the ConcurrentMap interface:

  • ConcurrentHashMap

ConcurrentHashMap

The ConcurrentHashMap is very similar to the java.util.HashTable class, except that ConcurrentHashMap offers better concurrency than HashTable does.

ConcurrentHashMap does not lock the Map while you are reading from it. Additionally, ConcurrentHashMap does not lock the entire Map when writing to it. It only locks the part of the Map that is being written to, internally.

Another difference is that ConcurrentHashMap does not throw ConcurrentModificationException if the ConcurrentHashMap is changed while being iterated. The Iterator is not designed to be used by more than one thread though.

ConcurrentMap Example

Here is an example of how to use the ConcurrentMap interface. The example uses a ConcurrentHashMap implementation:

ConcurrentMap concurrentMap = new ConcurrentHashMap();

concurrentMap.put("key", "value");

Object value = concurrentMap.get("key");

Avoiding Slipped Conditions

Even though the methods of a ConcurrentHashMap are thread safe - you can still run into concurrency problems if you using it the "wrong way".

Here, I will show you how using a ConcurrentHashMap in the "wrong way" can lead to Slipped Conditions . Look at this Java code example:

ConcurrentMap map = new ConcurrentHashMap();

if( !map.containsKey("key1") ) {
     map.put("key1", "value1");
}

Even if both the containsKey() and put() method are both thread safe - the above if-statement construct is not thread safe.

The problem with the above if-construct is, that if 2 threads execute the above if-statement simultaneously, they may both call map.containsKey("key1") at the same time, they might both receive the answer that the map does not contain the key "key1" - meaning containsKey() returns false. In that case, both threads will continue into the if-statement body, and both insert a value into the ConcurrentMap. The second thread to insert its value will overwrite the value inserted by the first thread.

In the above example, having 2 threads both insert the static value "value1" may not be so big of a problem, but what if the value was computed - based on something the individual thread knows?

The solution to this problem is to use one of the atomic methods putIfAbsent() or computeIfAbsent() instead.

The putIfAbsent() method of the Java ConcurrentMap interface inserts the given key + value pair, if no key + value pair exists for the given key already. Here is an example of using the putIfAbsent() method:

ConcurrentMap map = new ConcurrentHashMap();

map.putIfAbsent("key1", "value1");

As you can see, there is now only a single method call to the ConcurrentMap - the putIfAbsent() method call. The ConcurrentMap implementation will make sure that only one thread at a time will be allowed to insert a value for the same key. The ConcurrentMap might allow multiple threads to insert key + value pairs for different keys - depending on its internal implementation (e.g. some implementations may only allow multiple concurrent insertions if the key + value pair lands in different "buckets" internally).

The computeIfAbsent() method is similar to the putIfAbsent() method - except it enables you to compute a value to insert for a given key, if no key + value pair is already stored for that key. Here is an example of using the computeIfAbsent() method:

ConcurrentMap map = new ConcurrentHashMap();

concurrentMap2.computeIfAbsent("key2", (key) -> {
    return "Value for key " + key.toString() + " : " + Thread.currentThread.getName();
});

This example computes and inserts a value for the given key, if the given key is already stored in the ConcurrentMap.

Only one thread at a time will be allowed to execute computeIfAbsent() for the same key. If two threads calls computeIfAbsent() for the same absent key, one of the threads will be allowed to compute and insert its value, and the other thread will not compute nor insert any value.

The value to be inserted is computed by the Java lambda expression that you pass in as the second parameter to the computeIfAbsent() method.

The Java ConcurrentMap interface also has a computeIfPresent() method, which will compute and insert a value into the ConcurrentMap if a given key is already present in the map. If not present, no value is computed nor inserted into the ConcurrentMap. Here is an example of using the computeIfPresent() method:

ConcurrentMap map = new ConcurrentHashMap();

concurrentMap2.computeIfPresent("key2", (key) -> {
    return "Value for key " + key.toString() + " : " + Thread.currentThread.getName();
});

In the example above, the ConcurrentMap does not contain any keys at all at the time the computeIfPresent() method is called, so no value is computed nor inserted into the ConcurrentMap.

Jakob Jenkov

Featured Videos

Java ConcurrentMap + ConcurrentHashMap

Java Generics

Java ForkJoinPool

P2P Networks Introduction

















Close TOC
All Tutorial Trails
All Trails
Table of contents (TOC) for this tutorial trail
Trail TOC
Table of contents (TOC) for this tutorial
Page TOC
Previous tutorial in this tutorial trail
Previous
Next tutorial in this tutorial trail
Next