1. Segment 数组
ConcurrentHashMap 内部维护一个 Segment 数组,每个 Segment 都是一个小型的 HashMap。Segment 继承自 ReentrantLock,因此每个 Segment 都是一个可重入锁。
2. 并发级别
ConcurrentHashMap 在构造时可以指定并发级别(concurrencyLevel),该值决定了 Segment 数组的大小。默认情况下,并发级别为 16。
3. hash 分段
当向 ConcurrentHashMap 中添加元素时,首先根据 key 的 hash 值确定该元素属于哪个 Segment。这样可以将锁的粒度缩小到 Segment 级别,而不是整个 Map。
4. 锁定 Segment
在对某个 Segment 进行写操作时,需要获取该 Segment 的锁。读操作则不需要加锁,因为读操作是在 Segment 内部进行的。
5. 扩容
当某个 Segment 中的元素数量超过阈值时,该 Segment 会进行扩容。扩容时,需要获取该 Segment 的锁,以确保线程安全。
性能优化怎么做的?
1. 分段锁(Segment Locking)
ConcurrentHashMap 将整个哈希表分成多个 Segment,每个 Segment 是一个独立的锁。这样,多个线程可以并发地访问不同的 Segment,从而减少了锁的竞争。
- 锁粒度: 通过将锁的粒度从整个 Map 降低到每个 Segment,ConcurrentHashMap 允许多个线程同时对不同的 Segment 进行读写操作。
- 并发级别: 在构造 ConcurrentHashMap 时,可以指定并发级别(concurrencyLevel),这决定了 Segment 的数量。默认值为 16,意味着最多可以有 16 个线程同时进行写操作。
2. 无锁读操作
在读操作时,ConcurrentHashMap 不需要加锁。它使用了以下机制来确保线程安全和数据一致性:
- 使用 volatile 变量: 读操作直接读取数据,使用 volatile 关键字确保内存可见性。这样,即使在没有锁的情况下,其他线程对数据的修改也能被及时看到。
- 快速访问: 由于读操作不需要加锁,多个线程可以快速地并发读取数据,极大地提高了读操作的性能。
3. CAS 操作(Compare-And-Swap)
在写操作中,ConcurrentHashMap 使用 CAS 操作来更新数据,这是一种无锁的并发控制机制。
- 原子性更新: CAS 操作可以在不加锁的情况下安全地更新值。它通过比较当前值和预期值,如果相同则更新为新值,从而确保了原子性。
- 减少锁竞争: 通过使用 CAS,ConcurrentHashMap 能够在高并发环境下减少锁的竞争,从而提高性能。
4. 扩容机制
ConcurrentHashMap 的扩容设计也经过了优化,以支持高效的并发扩容。
- 分段扩容: 当某个 Segment 的元素数量超过阈值时,仅扩容该 Segment,而不是整个 Map。这避免了在扩容期间对整个 Map 的锁定。
- 并发扩容: 在扩容过程中,允许多个线程同时进行扩容操作。通过将扩容过程分散到多个线程,减少了扩容带来的性能影响。