HashMap
在 Java 中是非线程安全的数据结构,这意味着如果多个线程同时访问和修改同一个 HashMap
实例,可能会导致数据不一致或者其他并发问题。为了解决这个问题,Java 提供了 ConcurrentHashMap
,它是线程安全的哈希表实现。
ConcurrentHashMap
实现线程安全的方式有以下几个主要特点:
- 分段锁:
ConcurrentHashMap
内部使用了一种叫做分段锁(Segment)的机制。它将整个哈希表分成多个段(Segment),每个段内部都有一个独立的锁,不同段之间的数据修改操作可以并行进行,从而提高并发性能。 - volatile 修饰符:
ConcurrentHashMap
使用了volatile
修饰符来保证内部的数组和链表结构对于多线程是可见的。这样在进行读操作时,不需要加锁,从而提高读操作的性能。 - CAS 操作:
ConcurrentHashMap
使用了 CAS(Compare and Swap)操作来进行数据的修改。CAS 是一种无锁算法,通过比较内存中的值与预期值是否相等来确定是否修改。这样可以避免传统锁带来的性能开销。 - 复合操作的原子性:
ConcurrentHashMap
中的一些操作,比如putIfAbsent
、remove
等,是原子的复合操作,可以保证在单个方法调用中完成操作,避免了需要多次方法调用和手动加锁。
关于 size
方法的线程安全实现:
ConcurrentHashMap
的 size
方法在实现上会做出一些权衡,以在不需要锁住整个数据结构的情况下获取近似的大小。因为获取精确的大小需要遍历整个数据结构,这会带来性能开销,特别是在高并发环境下。
ConcurrentHashMap
使用一种称为“段级锁”的机制来控制大小计算的并发性。在 ConcurrentHashMap
中,每个段内都有一个计数器来记录该段内的键值对数量,size
方法会遍历所有的段,并将各段内的计数器值相加得到总的近似大小。因此,虽然不能保证 size
方法返回的值一定是精确的,但它是一个高效的近似值,适用于大多数场景。
需要注意的是,虽然 ConcurrentHashMap
在很多情况下可以提供线程安全的操作,但在某些特定场景下,仍然需要额外的同步操作。