点击蓝字
关注我们 FOLLOW US
ConcurrentHashMap类是在在JDK 1.5中引入的,属于java.util.concurrent包,它也实现了ConcurrentMap以及Serializable接口。ConcurrentHashMap是对HashMap的增强,因为我们知道在我们的应用程序中处理Threads时,HashMap并不是一个很好的选择,因为从性能上来说,HashMap并不符合要求。
ConcurrentHashMap的关键点:
ConcurrentHashMap的下划线数据结构是Hashtable。
ConcurrentHashMap类是线程安全的,即多个线程可以对一个对象进行操作,而不会产生任何复杂的问题。
任何数量的线程都可以同时进行读取操作,而不会锁定ConcurrentHashMap对象,这在HashMap中是不存在的。
在ConcurrentHashMap中,对象根据并发级别被划分为若干段。
ConcurrentHashMap的默认并发级别是16。
在ConcurrentHashMap中,一次可以有任意多个线程进行检索操作,但对于对象中的更新,线程必须锁定该线程要操作的特定段。这种类型的锁定机制被称为分段锁或桶锁。因此每次线程可以进行16次更新操作。
在ConcurrentHashMap中不可能插入Null作为键或值。
Declaration:
public class ConcurrentHashMap<K,•V> extends AbstractMap<K,•V> implements ConcurrentMap<K,•V>, Serializable
这里,K是键对象类型,V是值对象类型。
ConcurrentHashMap的层次结构
它实现了 Serializable, ConcurrentMap, Map 接口,继承了AbstractMap类
ConcurrentHashMap的构造函数
并发级别 它是指同时更新地图的线程数量。实现中会执行内部大小调整,以尽量适应这个数量的线程。
Load-Factor(负载系数):这是一个阈值,用于控制大小调整。
初始容量 是一个阈值,用来控制调整大小。如果这个地图的容量是10,就意味着它可以存储10个条目。就意味着它可以存储10个条目。
1. ConcurrentHashMap() : 创建一个新的空Map,默认初始容量(16)、负载因子(0.75)和并发级别(16)。
ConcurrentHashMap<K, V> chm = new ConcurrentHashMap<>();
2. ConcurrentHashMap(int initialCapacity) : 用指定的初始容量创建一个新的空Map,并使用默认的负载因子(0.75)和并发级别(16)。
ConcurrentHashMap chm = new ConcurrentHashMap<>(int initialCapacity);
3. ConcurrentHashMap(int initialCapacity, float loadFactor) : 用指定的初始容量和负载因子以及默认的并发级别(16)创建一个新的空Map。
ConcurrentHashMap chm = new ConcurrentHashMap<>(int initialCapacity, float loadFactor);
4. ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel); 用指定的初始容量、负载因子和并发级别创建一个新的空Map。
ConcurrentHashMap chm = new ConcurrentHashMap<>(int initialCapacity, float loadFactor, int concurrencyLevel);
5. ConcurrentHashMap(Map m); 创建一个与给定ConcurrentHashMap相同的新Map。
ConcurrentHashMap chm = new ConcurrentHashMap<>(Map m);
Example:
// Java program to demonstrate working of ConcurrentHashMap import java.util.concurrent.*; class ConcurrentHashMapDemo { public static void main(String[] args) { // create an instance of // ConcurrentHashMap ConcurrentHashMap m = new ConcurrentHashMap<>(); // Insert mappings using // put method m.put(100, "Hello"); m.put(101, "Geeks"); m.put(102, "Geeks"); // Here we cant add Hello because 101 key // is already present in ConcurrentHashMap object m.putIfAbsent(101, "Hello"); // We can remove entry because 101 key // is associated with For value m.remove(101, "Geeks"); // Now we can add Hello m.putIfAbsent(103, "Hello"); // We cant replace Hello with For m.replace(101, "Hello", "For"); System.out.println(m); } }
Output:
{100=Hello, 102=Geeks, 103=Hello}
ConcurrentHashMap常用操作
1. 添加元素
要将映射数据插入到一个ConcurrentHashMap中,我们可以使用put()或putAll()方法。下面的示例代码解释了这两种方法。
// Java program to demonstrate adding // elements to the ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap; import java.util.*; public class AddingElementsToConcuurentHashMap { public static void main(String[] args) { // Creating ConcurrentHashMap ConcurrentHashMap<String, String> my_cmmap = new ConcurrentHashMap<String, String>(); // Adding elements to the map // using put() method my_cmmap.put("1", "1"); my_cmmap.put("2", "1"); my_cmmap.put("3", "1"); my_cmmap.put("4", "1"); my_cmmap.put("5", "1"); my_cmmap.put("6", "1"); // Printing the map System.out.println("Mappings of my_cmmap : " + my_cmmap); // create another concurrentHashMap ConcurrentHashMap<String, String> new_chm = new ConcurrentHashMap<>(); // copy mappings from my_cmmap to new_chm new_chm.putAll(my_cmmap); // Displaying the new map System.out.println("New mappings are: " + new_chm); } }
Output
Mappings of my_cmmap : {1=1, 2=1, 3=1, 4=1, 5=1, 6=1}New mappings are: {1=1, 2=1, 3=1, 4=1, 5=1, 6=1}
2. 删除元素
要删除一个映射,我们可以使用 ConcurrentHashmap 类的 remove(Object key) 方法。如果key在map中不存在,那么这个函数什么也不做。要清除整个映射,我们可以使用clear()方法。
// Java program to demonstrate removing // elements from ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap; import java.util.*; public class RemoveElementsFromConcurrentHashMap { public static void main(String[] args) { // Creating ConcurrentHashMap Map my_cmmap = new ConcurrentHashMap(); // Adding elements to the map // using put() method my_cmmap.put("1", "1"); my_cmmap.put("2", "1"); my_cmmap.put("3", "1"); my_cmmap.put("4", "1"); my_cmmap.put("5", "1"); my_cmmap.put("6", "1"); // Printing the map System.out.println("Map: " + my_cmmap); System.out.println(); // Removing the mapping // with existing key 6 // using remove() method String valueRemoved = my_cmmap.remove("6"); // Printing the map after remove() System.out.println("After removing mapping with key 6:"); System.out.println("Map: " + my_cmmap); System.out.println("Value removed: " + valueRemoved); System.out.println(); // Removing the mapping // with non-existing key 10 // using remove() method valueRemoved = my_cmmap.remove("10"); // Printing the map after remove() System.out.println( "After removing mapping with key 10:"); System.out.println("Map: " + my_cmmap); System.out.println("Value removed: " + valueRemoved); System.out.println(); // Now clear the map using clear() my_cmmap.clear(); // Print the clea Map System.out.println("Map after use of clear(): " + my_cmmap); } }
Output
Map: {1=1, 2=1, 3=1, 4=1, 5=1, 6=1}After removing mapping with key 6:Map: {1=1, 2=1, 3=1, 4=1, 5=1}Value removed: 1After removing mapping with key 10:Map: {1=1, 2=1, 3=1, 4=1, 5=1}Value removed: nullMap after use of clear(): {}
3. 获取元素
我们可以使用get()方法来访问一个ConcurrentHashMap的元素,下面给出了这个例子。
// Java Program Demonstrate accessing // elements of ConcurrentHashMap import java.util.concurrent.*; class AccessingElementsOfConcurrentHashMap { public static void main(String[] args) { // create an instance of ConcurrentHashMap ConcurrentHashMap chm = new ConcurrentHashMap(); // insert mappings using put method chm.put(100, "Geeks"); chm.put(101, "for"); chm.put(102, "Geeks"); chm.put(103, "Contribute"); // Displaying the HashMap System.out.println("The Mappings are: "); System.out.println(chm); // Display the value of 100 System.out.println("The Value associated to " + "100 is : " + chm.get(100)); // Getting the value of 103 System.out.println("The Value associated to " + "103 is : " + chm.get(103)); } }
Output
The Mappings are: {100=Geeks, 101=for, 102=Geeks, 103=Contribute}The Value associated to 100 is : GeeksThe Value associated to 103 is : Contribute
4. 遍历
我们可以使用Iterator接口来遍历Collection Framework的任何结构。由于Iterators只处理一种类型的数据,我们使用Entry< ? , ? > 将两种不同类型的数据解析成兼容的格式。然后使用 next() 方法打印 ConcurrentHashMap 的元素。
// Java Program for traversing a // ConcurrentHashMap import java.util.concurrent.*; import java.util.*; public class TraversingConcurrentHashMap { public static void main(String[] args) { // create an instance of ConcurrentHashMap ConcurrentHashMapString> chmap = new ConcurrentHashMapString>(); // Add elements using put() chmap.put(8, "Third"); chmap.put(6, "Second"); chmap.put(3, "First"); chmap.put(11, "Fourth"); // Create an Iterator over the // ConcurrentHashMap IteratorString> > itr = chmap.entrySet().iterator(); // The hasNext() method is used to check if there is // a next element The next() method is used to // retrieve the next element while (itr.hasNext()) { ConcurrentHashMap.EntryString> entry = itr.next(); System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); } } }
Output
Key = 3, Value = FirstKey = 6, Value = SecondKey = 8, Value = ThirdKey = 11, Value = Fourth
Methods of ConcurrentHashMap
K – Map集合中键的类型
V – Map中映射的值的类型。
METHOD | DESCRIPTION |
---|---|
clear() | 移除此Map上的所有键值对。 |
compute(K key, BiFunction remappingFunction) | 试图计算指定键及其当前映射值的映射(如果没有当前映射,则为空)。 |
computeIfAbsent(K key, Function mappingFunction) | 如果指定的键还没有与值相关联,则尝试使用给定的映射函数计算其值,并将其输入到这个映射中,除非为空。 |
computeIfPresent(K key, BiFunction super K,? super V,? extends V> remappingFunction) | 如果指定键的值存在,则尝试根据键及其当前映射值计算新的映射。 |
contains(Object value) | 测试某个键是否映射到本表的指定值中。 |
containsKey(Object key) | 测试指定的对象是否是本集合中的键。 |
containsValue(Object value) | 如果此映射将一个或多个键映射到指定的值,则返回true。 |
elements() | 返回本表中数值的枚举。 |
entrySet() | 返回该地图中包含的映射的Set视图。 |
equals(Object o) | 将指定的对象与此图进行平等比较。 |
forEach(long parallelismThreshold, BiConsumer super K,? super V> action) | 对每个(键,值)执行给定的操作。 |
forEach(long parallelismThreshold, BiFunction super K,? super V,? extends U> transformer, Consumer super U> action) | 对每个(键,值)的非空变换执行给定的操作。 |
forEachEntry(long parallelismThreshold, Consumer super Map.Entry> action) | 对每个条目执行指定的操作。 |
forEachEntry(long parallelismThreshold, Function,? extends U> transformer, Consumer super U> action) | 对每个条目的非空变换执行给定动作。 |
forEachKey(long parallelismThreshold, Consumer super K> action) | Performs the given action for each key. |
forEachKey(long parallelismThreshold, Function super K,? extends U> transformer, Consumer super U> action) | Performs the given action for each non-null transformation of each key. |
forEachValue(long parallelismThreshold, Consumer super V> action) | Performs the given action for each value. |
forEachValue(long parallelismThreshold, Function super V,? extends U> transformer, Consumer super U> action) | Performs the given action for each non-null transformation of each value. |
get(Object key) | 返回指定键的映射值,如果该映射不包含键的映射,则返回空值。 |
getOrDefault(Object key, V defaultValue) | Returns the value to which the specified key is mapped, or the given default value if this map contains no mapping for the key. |
hashCode() | 返回此Map的哈希码值,即Map中每个键值对的key.hashCode() ^ value.hashCode()之和。 |
keys() | 返回本表中键的枚举。 |
keySet() | 返回此地图中的键值的Set视图。 |
keySet(V mappedValue) | Returns a Set view of the keys in this map, using the given common mapped value for any additions (i.e., Collection.add(E) and Collection.addAll(Collection)). |
mappingCount() | Returns the number of mappings. |
merge(K key, V value, BiFunction super V,? super V,? extends V> remappingFunction) | If the specified key is not already associated with a (non-null) value, associates it with the given value. |
newKeySet() | Creates a new Set backed by a ConcurrentHashMap from the given type to Boolean.TRUE. |
newKeySet(int initialCapacity) | Creates a new Set backed by a ConcurrentHashMap from the given type to Boolean.TRUE. |
put(K key, V value) | 将指定的键映射到本表的指定值上。 |
putAll(Map m) | 将指定地图上的所有映射复制到这个地图上。 |
putIfAbsent(K key, V value) | 如果指定的键没有与值关联,则将其与给定的值关联。 |
remove(Object key) | 从该地图中删除键(和其对应的值)。 |
remove(Object key, Object value) | 只有在当前映射到给定值的情况下,才会删除某个键的条目。 |
values() | 返回该Map中包含的值的集合视图。 |
java.util.AbstractMap类中方法的定义
METHOD | DESCRIPTION |
---|---|
clone() | 返回这个AbstractMap实例的一个浅复制:键和值本身不会被克隆。 |
isEmpty() | 如果该Map不包含键值对,则返回true。 |
size() | 返回该Map中键值对的数量。 |
java.util.concurrent.ConcurrentMap接口中方法的定义
METHOD | DESCRIPTION |
---|---|
forEach(BiConsumer super K,? super V> action) | 对Map中的每个条目执行给定的操作,直到所有的条目都被处理完,或者该操作引发一个异常。 |
replaceAll(BiFunction super K,? super V,? extends V> function) | 用在该条目上调用给定函数的结果替换每个条目的值,直到所有条目都被处理完毕或函数引发异常。 |
Must Read: Difference between HashMap and ConcurrentHashMap
ConcurrentHashMap vs Hashtable
HashTable
Hashtable 是Map数据结构的实现
这是一个遗留类,其中所有方法都是在Hashtable实例上使用synchronized关键字进行同步。
由于它的方法是同步的,所以是线程安全的。
ConcurrentHashMap
ConcurrentHashMap 实现了Map数据结构,也提供了像Hashtable一样的线程安全。
它的工作原理是将完整的哈希数组划分为片段或部分,并允许对这些片段进行并行访问。
锁是在一个更细粒度的哈希桶。
当你的程序中需要非常高的并发性时,请使用ConcurrentHashMap。
它是一个不需要同步整个Map的线程安全。
读取可以非常快地进行,而写入则是在段级或桶级上加锁完成。
在对象级别上没有锁。
如果一个线程试图在另一个线程迭代时修改它,ConcurrentHashMap不会抛出ConcurrentModificationException。
ConcurrentHashMap不允许NULL值,所以在ConcurrentHashMap中key不能为空。
如果一个线程试图修改它,而另一个线程正在迭代它,ConcurrentHashMap不会抛出一个并发修改异常。
PROPERTIES | HASHTABLE | CONCURRENTHASHMAP |
---|---|---|
Creation | Map ht = new Hashtable(); | Map chm = new ConcurrentHashMap(); |
Is Null Key Allowed ? | No | No |
Is Null Value Allowed ? | No | Yes |
Is Thread Safe ? | Yes | Yes, Thread safety is ensured by having separate locks for separate buckets, resulting in better performance. Performance is further improved by providing read access concurrently without any blocking. |
Performance | Slow due to synchronization overhead. | Faster than Hashtable. ConcurrentHashMap is a better choice when there are more reads than writes. |
Iterator | Hashtable uses enumerator to iterate the values of Hashtable object. Enumerations returned by the Hashtable keys and elements methods are not fail-fast. | Fail-safe iterator: Iterator provided by the ConcurrentHashMap is fail-safe, which means it will not throw ConcurrentModificationException. |
结论:
如果需要一个线程安全的高并发实现,那么建议使用ConcurrentHashMap来代替Hashtable。