ConcurrentHashMap概述
ConcurrentHashMap是Java 并发包(java.util.concurrent)中提供的一个线程安全且高效的哈希表实现,用于在并发环境中存储键值对,它允许在多个线程之间安全地共享和修改数据,使得开发者无需显式同步代码就能在并发环境下安全使用。与同样是线程安全的哈希表HashTable相比,ConcurrentHashMap具有更高的并发效率。
ConcurrentHashMap的实现
Java7中的实现
在 Java7中,ConcurrentHashMap使用了分段锁(Segment)的技术来实现并发访问的安全性。这个设计思想是将整个ConcurrentHashMap分成若干个Segment(默认为16),每个Segment相当于一个小型的哈希表,拥有自己的锁。这样,在进行put操作时,只需要锁定当前操作Segment而不是整个 Map,多个线程就能同时操作不同的段,在并发环境下将实现更高的吞吐量,而在单线程环境下只损失非常小的性能。Segment使用可重入锁ReentrantLock实现。
Java8中的实现
在 Java8中对ConcurrentHashMap进行了重大改进,具体包括进一步细化了锁粒度,摒弃了原有的分段锁的设计,转而采用了CAS+synchronized的同步方式,锁住的不再是一个段,而是某个节点,即只有HashCode相同的元素才可能阻塞,进一步提高了并发性能。与HashMap一样,Java8中ConcurrentHashMap底层数据结构也由数组+链表改为了数组+链表+红黑树。至于引入红黑树的理由我想和HashMap是一样的。之前写了一篇关于HashMap文章有详细介绍,请参考:【Java集合】HashMap
示例代码
ConcurrentHashMap使用增强for循环或forEach遍历集合时可以删除元素,而不像HashMap、ArrayList等其它集合需要使用迭代器。遍历集合时删除元素问题
public static void main(String[] args) {
Map<String, Integer> conMap = new ConcurrentHashMap<>();
for (int i = 0; i < 10; i++) {
conMap.put("key"+i, i);
}
for (Map.Entry<String, Integer> entry : conMap.entrySet()) {
int v = entry.getValue();
if (v < 5) {
conMap.remove(entry.getKey());
}
}
System.out.println(conMap);
}
//输出
{key5=5, key6=6, key9=9, key7=7, key8=8}
一个线程对ConcurrentHashMap执行插入,另一个线程可以实时读取,ConcurrentHashMap保证了线程之间的安全性和可见性。
public static void main(String[] args) {
Map<String, Integer> conMap = new ConcurrentHashMap<>();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
conMap.put("key"+i, i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(() -> {
while (true){
System.out.println("size="+conMap.size());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread1.start();
thread2.start();
}
//输出
size=0
size=1
size=2
size=3
size=4
size=5
size=6
size=7
size=8
size=9
size=10
size=10
size=10
...
总结
总之ConcurrentHashMap是一个高性能的Java并发容器,是多线程下常用的map容器,其使用方法基本可以参考HashMap。