Course Content
Multithreading in Java
Multithreading in Java
ConcurrentMap and its Implementations
Real Life Example
A web application uses ConcurrentMap
to cache frequently requested data such as user sessions. Different threads can simultaneously update and read data from the map, ensuring fast access and secure operations.
Differences from Other Types
- Security in a multi-threaded environment:
ConcurrentMap
automatically handles synchronization of data accesses, while in conventionalMap
this task must be done manually; - Efficiency: Allows reading and writing data in parallel without locking the entire data structure.
ConcurrentMap Implementations
ConcurrentHashMap
: Efficiently supports multiple threads by dividing the map into segments (buckets), allowing parallel execution of operations without locking the entire map.
Syntax
Main
ConcurrentMap<Integer, Integer> concurrentHashMap = new ConcurrentHashMap<>();
ConcurrentHashMap
in Java divides data into multiple buckets, each managed by a separate monitor. This setup allows different threads to modify or read data from different buckets simultaneously, which enhances performance.
Threads can access buckets in parallel, reducing locks and avoiding data races.
Each bucket contains records in the form of key-value pairs, which can be organized as linked lists.
ConcurrentSkipListMap
: A skip-list based implementation that supports sorted key ordering. Provides fast insertion, deletion, and access to data in a multithreaded environment.
Syntax
Main
ConcurrentMap<Integer, Integer> concurrentSkipListMap = new ConcurrentSkipListMap<>();
📝Insert: When a new item is added to ConcurrentSkipListMap
, it starts at the lowest level. It then moves up through the levels until it is placed where its keys and values are in the correct order.
🔍Search: To find an item by key, ConcurrentSkipListMap
begins at the head node of the topmost level. It follows the pointers until it locates a node with a key that is equal to or greater than the search key.
❌Deletion: To delete an item from ConcurrentSkipListMap
, it is first removed from the lowest level. It is then downgraded through the levels until it is removed from where its keys and values are correctly ordered.
Example of using ConcurrentMap in code
Main Methods
putIfAbsent(K key, V value)
: Adds a key-value pair to the map only if the key is not already present.
Main
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(); map.putIfAbsent("a", 1); // Adds the pair ("a", 1) to the map, as "a" is not already present map.putIfAbsent("a", 2); // Does not change the value, as "a" is already present in the map
remove(Object key, Object value)
: Removes the key-value pair if the key is mapped to the specified value.
Main
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("a", 1); map.remove("a", 1); // Removes the pair ("a", 1), as "a" is mapped to value 1 map.remove("a", 2); // Does nothing, as "a" is not mapped to value 2
replace(K key, V value)
: Replaces the entry for a key only if it is currently mapped to some value.
Main
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("a", 1); map.replace("a", 2); // Replaces the value 1 with 2 for key "a" map.replace("b", 3); // Does nothing, as "b" is not present
compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
: Computes a new value for the specified key using the given remapping function, which might involve creating a new value, modifying, or removing the existing value.
Main
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("a", 1); map.compute("a", (k, v) -> v == null ? 1 : v + 1); // Increases the value for key "a" by 1 map.compute("b", (k, v) -> v == null ? 1 : v + 1); // Sets the value to 1 for new key "b"
merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)
: Merges the given value with the existing value associated with the key using the provided remapping function, which helps in aggregating data.
Main
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("a", 1); map.merge("a", 2, Integer::sum); // Sums the current value (1) with the new value (2), resulting in 3 map.merge("b", 2, Integer::sum); // Sets the value to 2 for new key "b"
getOrDefault(Object key, V defaultValue)
- returns the value associated with the specified key, or the default value if the key is not present.
Main
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("a", 1); int value1 = map.getOrDefault("a", 0); // Returns 1, as "a" is present int value2 = map.getOrDefault("b", 0); // Returns 0, as "b" is not present
😔 Limitations
One of the potential drawbacks is order instability, as some implementations might not guarantee the order of elements during iteration. Additionally, there can be limited support for certain operations; for example, atomic conditional updates may not be fully supported in some implementations.
💪 Advantages
On the positive side, high performance is a key benefit, making it well-suited for scenarios involving intensive read and write operations. It also offers ease of use, significantly reducing the need for manual synchronization management in a multi-threaded environment.
Thanks for your feedback!