一、ConcurrentHashMap介绍
ConcurrentHashMap 和 HashMap 思路是差不多的,但是因为它支持并发操作,所以要复杂一些。
整个 ConcurrentHashMap 由一个个 Segment 组成,Segment 代表”部分“或”一段“的意思,所以很多地方都会将其描述为分段锁。注意,行文中,我很多地方用了“槽”来代表一个 segment。
(简单理解就是,ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承 ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全)。
二、什么是Segment?
①、ConcurrentHashMap采用了非常精妙的"分段锁"策略,ConcurrentHashMap的主干是个Segment数组。
②、Segment继承了ReentrantLock,所以它就是一种可重入锁(ReentrantLock)。在ConcurrentHashMap,一个Segment就是一个子哈希表,Segment里维护了一个HashEntry数组,并发环境下,对于不同Segment的数据进行操作是不用考虑锁竞争的。(就按默认的ConcurrentLeve为16来讲,理论上就允许16个线程并发执行,有木有很酷)
所以,对于同一个Segment的操作才需考虑线程同步,不同的Segment则无需考虑。
Segment类似于HashMap,一个Segment维护着一个HashEntry数组。
三、ConcurrentHashMap常用方法介绍
1、clear() :从该映射中移除所有映射关系。
2、contains(Object value) :一种遗留方法,测试此表中是否有一些与指定值存在映射关系的键,返回值为Boolean。
3、containsKey(Object key) : 测试指定对象是否为此表中的键,返回值为Boolean。
4、containsValue(Object value) :如果此映射将一个或多个键映射到指定值,则返回 true。
5、entrySet() :返回此映射所包含的映射关系的 Set 视图,返回值为Set<Map.Entry<K,V>>。
6、get(Object key) :返回指定键所映射到的值,如果此映射不包含该键的映射关系,则返回 null。
7、isEmpty() : 如果此映射不包含键-值映射关系,则返回 true。
8、keys() :返回此表中键的枚举,返回值为 Enumeration。
9、keySet() : 返回此映射中包含的键的 Set 视图,返回值为 Set。
10、put(K key, V value) :将指定键映射到此表中的指定值。
11、remove(Object key) :从此映射中移除键(及其相应的值)。
12、remove(Object key, Object value) :只有目前将键的条目映射到给定值时,才移除该键的条目,返回值为Boolean。
13、replace(K key, V value) :只有目前将键的条目映射到某一值时,才替换该键的条目。
14、replace(K key, V oldValue, V newValue) :只有目前将键的条目映射到给定值时,才替换该键的条目,返回值为 boolean。
15、size() :返回此映射中的键-值映射关系数。
四、Java代码示例
package chapter3.concurrenthashmap;
import com.sun.org.apache.xpath.internal.operations.Bool;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author czd
*/
public class ConcurrentHashMapTest {
public static void main(String[] args) {
/**
*构造方法摘要:
* 1.1、ConcurrentHashMap():
* 创建一个带有默认初始容量 (16)、加载因子 (0.75) 和 concurrencyLevel (16) 的新的空映射。
* 1.2、ConcurrentHashMap(int initialCapacity):
* 创建一个带有指定初始容量、默认加载因子 (0.75) 和 concurrencyLevel (16) 的新的空映射。
*/
ConcurrentHashMap<String,Integer> concurrentHashMap = new ConcurrentHashMap<>(10);
/**
* 1、 put(K key, V value):将指定键映射到此表中的指定值
*/
for (int i = 0; i < 20; i++){
concurrentHashMap.put("" + i , i);
}
/**
* 2、entrySet():返回此映射所包含的映射关系的 Set 视图,返回Set<Map.Entry<K,V>>
*
*/
final Set<Map.Entry<String, Integer>> entries = concurrentHashMap.entrySet();
for(Map.Entry<String , Integer> entry : entries){
System.out.println("Key:" + entry.getKey() + " Value:" + entry.getValue());
}
/**
* 注意:一般使用如下方法遍历ConcurrentHashMap
*/
// for(Map.Entry<String , Integer> entry : concurrentHashMap.entrySet()){
// System.out.println("Key:" + entry.getKey() + " Value:" + entry.getValue());
// }
/**
* 3、contains(Object value) :一种遗留方法,测试此表中是否有一些与指定值存在映射关系的键,返回值为Boolean。
*/
Boolean contains2Boolean = concurrentHashMap.contains(2);
System.out.println("concurrentHashMap是否包含Value值为2的值:" + contains2Boolean);
Boolean contains15Boolean = concurrentHashMap.contains(15);
System.out.println("concurrentHashMap是否包含Value值为15的值:" + contains15Boolean);
/**
* 4、ontainsKey(Object key) :测试指定对象是否为此表中的键,返回值为Boolean。
*/
Boolean contains2Key = concurrentHashMap.containsKey("2");
System.out.println("concurrentHashMap是否包含Key值为2的键:" + contains2Key);
Boolean contains15Key = concurrentHashMap.containsKey("15");
System.out.println("concurrentHashMap是否包含Key值为15的键:" + contains15Key);
/**
* 5、containsValue(Object value) :如果此映射将一个或多个键映射到指定值,则返回 true。
*/
Boolean contains3Boolean = concurrentHashMap.containsValue(3);
System.out.println("concurrentHashMap是否包含Value值为3的值:" + contains3Boolean);
Boolean contains16Boolean = concurrentHashMap.containsValue(16);
System.out.println("concurrentHashMap是否包含Value值为16的值:" + contains16Boolean);
/**
* 6、isEmpty():如果此映射不包含键-值映射关系,则返回 true。
*/
Boolean isEmptyBoolean = concurrentHashMap.isEmpty();
System.out.println("concurrentHashMap是否为空:" + isEmptyBoolean);
/**
* 7、keySet() :返回此映射中包含的键的 Set 视图。
*/
Set<String> keySet = concurrentHashMap.keySet();
for (String key : keySet){
System.out.println("concurrentHashMap中的Key键:" + key);
}
/**
* 8、remove(Object key):从此映射中移除键(及其相应的值)。
*/
Integer beforeRemove = concurrentHashMap.get("10");
System.out.println("beforeRemove: " + beforeRemove);
concurrentHashMap.remove("10");
Integer afterRemove = concurrentHashMap.get("10");
System.out.println("afterRemove: " + afterRemove);
/**
* 9、remove(Object key, Object value):只有目前将键的条目映射到给定值时,才移除该键的条目。
*/
Integer beforeMove = concurrentHashMap.get("9");
System.out.println("beforeRemove: " + beforeMove);
boolean removeError = concurrentHashMap.remove("9", 8);
boolean removeTrue = concurrentHashMap.remove("9", 9);
System.out.println("键值匹配>>>移除成功?:" + removeTrue + " 键值不匹配>>>移除成功?:" + removeError);
Integer afterMove = concurrentHashMap.get("9");
System.out.println("afterMove: " + afterMove);
/**
* 10、replace(K key, V value) :只有目前将键的条目映射到某一值时,才替换该键的条目。
*/
Integer beforeReplace = concurrentHashMap.get("8");
System.out.println("beforeReplace: " + beforeReplace);
concurrentHashMap.replace("8" , 28);
Integer afterReplace = concurrentHashMap.get("8");
System.out.println("afterReplace: " + afterReplace);
/**
* 11、replace(K key, V oldValue, V newValue):
* 只有目前将键的条目映射到给定值时,才替换该键的条目,返回值为Boolean。
*/
Integer beforeRep = concurrentHashMap.get("6");
System.out.println("beforeRep: " + beforeRep);
Boolean replaceError = concurrentHashMap.replace("6" , 16 , 26);
Boolean replaceTrue = concurrentHashMap.replace("6" , 6 , 26);
System.out.println("键值匹配>>>替换成功?:" + replaceTrue + " 键值不匹配>>>替换成功?:" + replaceError);
Integer afterRep = concurrentHashMap.get("6");
System.out.println("afterRep: " + afterRep);
/**
* 12、size() :返回此映射中的键-值映射关系数。
*/
Integer size = concurrentHashMap.size();
System.out.println("关系数:" + size);
}
}
五、总结
ConcurrentHashMap代替同步的Map(Collections.synchronized(new HashMap())),众所周知,HashMap是根据散列值分段存储的,同步Map在同步的时候锁住了所有的段,而ConcurrentHashMap加锁的时候根据散列值锁住了散列值锁对应的那段,因此提高了并发性能。