1. Introduction
Even though Hashtable is thread safe, it is not very efficient. You can also create a synchronized collection using Collections.synchronizedMap, but it is also not efficient. If we want thread-safety with high throughput under high concurrency, you can use ConcurrentHashMap.
Following are some important points about ConcurrentHashMap:
ConcurrentHashMapis defined injava.util.concurrentpackage. This class is a member of the Java Collections Framework. This class implementsSerializable,ConcurrentMapandMapinterfaces.- All operations of this class are thread-safe.
- This class is similar to
HashTable. - This class does not provide the support to lock the entire table during access.
- Retrieval operations of this class does not result in locking and are non-blocking.
- Retrieval operations are non-blocking and can be used simultaneously with updated operations like
putandremove. Retrieval operations return the most recent updated value at that instant. - An update operation for a given key bears a happens-before relation with any (non-null) retrieval for that key.
- Iterators, Spliterators and Enumerations return elements reflecting most recent state at the time of its creation. They do not throw
ConcurrentModificationException. ConcurrentHashMapdoes not allownullas a key or a value.- The default concurrency-level of
ConcurrentHashMapis 16, that is 16 threads can simultaneously update theConcurrentHashMap.
2. Constructors
- ConcurrentHashMap()
- ConcurrentHashMap(int initialCapacity)
- ConcurrentHashMap(int initialCapacity, float loadFactor)
- ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel)
- ConcurrentHashMap(Map m)
The default values for ConcurrentHashMap are:
- initial capacity: 16
- load factor: 0.75
- concurrencyLevel: 16
The parameter concurrencyLevel is the the estimated number of concurrently updating threads. The implementation performs internal sizing to try to accommodate this many threads.
3. Some important methods of ConcurrentHashMap
| void clear() | Removes all of the mappings from this map. |
| boolean containsKey(Object key) | Checks if the specified object is a key in this table. |
| V get(Object key) | Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. |
| boolean isEmpty() | Returns true if this map contains no key-value mappings. |
| V put(K key, V value) | Maps the specified key to the specified value in this table. |
| V remove(Object key) | Removes the key (and its corresponding value) from this map. |
| int size() | Returns the number of key-value mappings in this map. |
| Collection values() | Returns a Collection view of the values contained in this map. |
4. Example of ConcurrentHashMap common operations
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) throws IOException {
ConcurrentHashMap cmap = new ConcurrentHashMap();
// add values
cmap.put("one", "1");
cmap.put("two", "2");
cmap.put("three", "3");
System.out.println(cmap); // {one=1, two=2, three=3}
// remove a value
cmap.remove("two");
System.out.println(cmap); // {one=1, three=3}
// replace a value
cmap.replace("one", "new value");
System.out.println(cmap); // {one=new value, three=3}
// size of map
System.out.println(cmap.size()); // 2
// value of map
System.out.println(cmap.values()); // [new value, 3]
// check if map is empty
System.out.println(cmap.isEmpty()); // false
// check if map contains a key
System.out.println(cmap.containsKey("one")); // true
// clear the map
cmap.clear();
System.out.println(cmap); // {}
}
}
5. Example of simultaneous read and write in ConcurrentHashMap
In this example, we’ll create a ConcurrentHashMap with one key. We’ll create two threads, one to update the value and the other to read the value. We’ll use a loop to read and write values simultaneously.
import java.util.concurrent.ConcurrentHashMap;
class MyThreadWrite implements Runnable {
ConcurrentHashMap cmap;
public MyThreadWrite(ConcurrentHashMap cmap) {
super();
this.cmap = cmap;
}
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " running. Writing value: " + i);
cmap.put("one", i);
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyThreadRead implements Runnable {
ConcurrentHashMap cmap;
public MyThreadRead(ConcurrentHashMap cmap) {
super();
this.cmap = cmap;
}
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " Running. Reading Value: " + this.cmap.get("one"));
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ConcurrentHashMapExample2 {
public static void main(String args[]) {
ConcurrentHashMap cmap = new ConcurrentHashMap();
Thread t1 = new Thread(new MyThreadRead(cmap));
Thread t2 = new Thread(new MyThreadWrite(cmap));
t1.start();
t2.start();
}
}
Output
Thread-0 Running. Reading Value: null Thread-1 running. Writing value: 0 Thread-0 Running. Reading Value: 0 Thread-1 running. Writing value: 1 Thread-1 running. Writing value: 2 Thread-0 Running. Reading Value: 2 Thread-1 running. Writing value: 3 Thread-0 Running. Reading Value: 2 Thread-0 Running. Reading Value: 3 Thread-1 running. Writing value: 4 Thread-0 Running. Reading Value: 4 ...........................
