ConcurrentHashMap
ConcurrentHashMap是Java中的一个线程安全的哈希表实现类。它继承自HashMap,具有与HashMap相似的功能,但在并发的场景下提供了更好的性能。
ConcurrentHashMap采用了锁分段技术来实现线程安全。它将哈希表分成多个段(Segment),每个段管理一部分的键值对。不同的线程可以同时访问不同的段,从而提高并发性能。
在ConcurrentHashMap中,每个段内部维护一个HashEntry数组,通过哈希值将键值对分别存储到不同的数组位置上。当多个线程同时访问不同的段时,它们可以同时进行读取操作,而对于同一段的写操作会进行同步处理。
ConcurrentHashMap还提供了一些其他的线程安全的操作,例如原子性的putIfAbsent、replace和remove等方法,以及批量操作的forEach和replaceAll方法。
CAS + Synchronized
ConcurrentHashMap是一个线程安全的哈希表,它使用了一种结合了CAS(Compare and Swap)和Synchronized(同步)的锁机制来保证线程安全性。
在ConcurrentHashMap中,CAS用于保证对于哈希表的读写操作的原子性。CAS是一种乐观锁机制,它通过比较内存中的值与期望值是否相等来确定该值是否被修改过。如果相等,则将新值写入内存,否则重新尝试。这样可以避免了传统的锁机制所带来的性能损失。
在ConcurrentHashMap中,Synchronized用于保证对于哈希表的结构修改操作的原子性。当需要对哈希表进行插入、删除、更新等操作时,使用Synchronized来保证同一时间只有一个线程能够修改哈希表的结构,从而避免了并发修改导致的数据不一致性。
ConcurrentHashMap使用CAS和Synchronized相结合的方式来保证线程安全。CAS保证了对于哈希表的读写操作的原子性,而Synchronized保证了对于哈希表的结构修改操作的原子性。这种结合方式既保证了线程安全,又尽可能地提高了并发性能。
重要内部类
ConcurrentHashMap 的重要内部类主要包括以下几种:
-
Segment:Segment 是 ConcurrentHashMap 的内部类,它本质上是一个 HashEntry 数组,用于存储键值对。ConcurrentHashMap 使用多个 Segment 来分割整个哈希表,每个 Segment 管理一部分键值对,不同的线程可以同时访问不同的 Segment,从而实现并发访问。
-
HashEntry:HashEntry 是 ConcurrentHashMap 内部用于存储键值对的节点。每个 HashEntry 包含一个 key 和一个 value,它们通过链表的方式连接在一起形成一个桶。当多个键值对的 hash 值相同时,它们会存储在同一个桶中,通过链表来解决哈希冲突。
-
HashIterator:HashIterator 是 ConcurrentHashMap 的内部迭代器,用于遍历 ConcurrentHashMap 中的键值对。它继承了抽象类 ConcurrentHashMap.Traverser,并实现了 Iterator 接口。HashIterator 通过遍历每个 Segment 中的 HashEntry 数组来实现对 ConcurrentHashMap 的遍历。
-
KeySetView:KeySetView 是 ConcurrentHashMap 的内部类,它实现了 Set 接口,用于获取 ConcurrentHashMap 的键的视图。KeySetView 通过内部类方式实现,包含了一个 ConcurrentHashMap 和一个共享的默认值。当对 KeySetView 进行操作时,会影响到 ConcurrentHashMap 中的键。
-
ValuesView:ValuesView 是 ConcurrentHashMap 的内部类,它实现了 Collection 接口,用于获取 ConcurrentHashMap 的值的视图。ValuesView 通过内部类方式实现,包含了一个 ConcurrentHashMap。当对 ValuesView 进行操作时,会影响到 ConcurrentHashMap 中的值。
-
EntrySetView:EntrySetView 是 ConcurrentHashMap 的内部类,它实现了 Set 接口,用于获取 ConcurrentHashMap 的键值对的视图。EntrySetView 通过内部类方式实现,包含了一个 ConcurrentHashMap。当对 EntrySetView 进行操作时,会影响到 ConcurrentHashMap 中的键值对。
重要操作
ConcurrentHashMap的重要操作包括:
- put(key, value): 将指定的键值对添加到ConcurrentHashMap中。
- get(key): 获取指定键的值。
- remove(key): 删除指定键的值。
- containsKey(key): 检查ConcurrentHashMap中是否包含指定的键。
- containsValue(value): 检查ConcurrentHashMap中是否包含指定的值。
- isEmpty(): 检查ConcurrentHashMap是否为空。
- size(): 返回ConcurrentHashMap中的键值对数量。
- keySet(): 返回ConcurrentHashMap中所有键的集合。
- values(): 返回ConcurrentHashMap中所有值的集合。
- entrySet(): 返回ConcurrentHashMap中所有键值对的集合。
- putIfAbsent(key, value): 在ConcurrentHashMap中仅在指定的键不存在时才添加指定的键值对。
- replace(key, oldValue, newValue): 替换ConcurrentHashMap中指定键的值,仅当当前值与旧值相等时才进行替换。
- replace(key, value): 替换ConcurrentHashMap中指定键的值。
- clear(): 清空ConcurrentHashMap中的所有键值对。
1.8 与 1.7的区别
在Java 1.7中,ConcurrentHashMap的内部实现使用了分段锁(Segment Locks)来实现并发操作。每个段(Segment)都可以看作是一个小的HashMap,它们仅控制特定区域的数据。这种设计可以降低并发操作之间的冲突,提高并发性能。
然而,在Java 1.8中,ConcurrentHashMap的内部实现变得更加简单和高效。在1.8中,ConcurrentHashMap使用了一种称为“基于CAS的非阻塞算法”的实现方式。这种实现方式不再使用分段锁,而是使用了一种称为“CAS(Compare and Swap)”的机制来实现并发操作。
CAS是一种乐观锁的机制,它允许多个线程同时进行自由读写操作。当线程尝试进行写操作时,CAS会先进行比较当前值和期望值是否相等,如果相等则进行更新操作,否则重新尝试。这种方式避免了锁的使用,减少了线程之间的竞争,从而提高了并发性能。
Java 1.8中的ConcurrentHashMap使用了一种基于CAS的非阻塞算法,相比于1.7中的分段锁机制,它的内部实现更加高效和简单。这使得1.8中的ConcurrentHashMap在并发操作下具有更好的性能表现。
实例
ConcurrentHashMap 是线程安全的哈希表实现,它支持高并发环境下的并发操作。下面是 ConcurrentHashMap 的 代码示例:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
// 创建一个 ConcurrentHashMap 对象
ConcurrentHashMap<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
// 在多线程环境下向 ConcurrentHashMap 中添加元素
Thread thread1 = new Thread(() -> {
concurrentHashMap.put("A", 1);
concurrentHashMap.put("B", 2);
concurrentHashMap.put("C", 3);
});
Thread thread2 = new Thread(() -> {
concurrentHashMap.put("D", 4);
concurrentHashMap.put("E", 5);
concurrentHashMap.put("F", 6);
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 输出 ConcurrentHashMap 的内容
concurrentHashMap.forEach((key, value) -> System.out.println(key + ": " + value));
}
}
上述示例中,首先创建了一个 ConcurrentHashMap 对象 concurrentHashMap
。
然后通过两个线程同时向 concurrentHashMap
中添加元素。由于 ConcurrentHashMap 的线程安全性,不会出现并发冲突的情况。
最后,使用 forEach
方法遍历 ConcurrentHashMap 的内容,并打印出每个键值对。
ConcurrentHashMap 内部使用了分段锁机制,将整个哈希表分成多个小的哈希表,每个小的哈希表对应一个锁。这样,在多线程环境下,如果多个线程分别操作不同的小哈希表,就可以同时进行,提高了并发性能。
需要注意的是,虽然 ConcurrentHashMap 是线程安全的,但并不意味着它可以代替所有的同步容器。在一些特定的场景下,可能需要使用其他的同步容器或并发集合类来满足具体的需求。
总结
ConcurrentHashMap是Java中线程安全的哈希表实现。它与HashMap相比,能够在多线程环境下提供更好的性能和并发访问的能力。
以下是ConcurrentHashMap的一些特点和总结:
-
线程安全:ConcurrentHashMap使用了锁分段技术,将整个数据结构分成多个段(Segment),每个段都有自己的锁。这意味着多个线程可以同时访问不同的段,从而提高了并发性能。
-
并发度高:ConcurrentHashMap的并发度是通过扩展段(Segment)来实现的。默认情况下,ConcurrentHashMap的并发度是16,即有16个段。
-
put操作是原子性的:ConcurrentHashMap的put操作是原子性的,不需要额外的同步措施。这意味着多个线程可以同时进行put操作,而不会出现数据不一致的问题。
-
读操作不需要加锁:由于ConcurrentHashMap使用了锁分段技术,所以读操作不需要加锁。这使得ConcurrentHashMap在读多写少的场景下表现良好。
-
适用于高并发场景:ConcurrentHashMap适用于高并发场景,特别是读多写少的场景。它可以提供更好的性能和并发访问能力。