面试题答案
一键面试性能差异
- 读操作性能:
- ConcurrentHashMap:在多线程环境下读操作性能较好。由于其采用分段锁等机制,允许多个线程同时进行读操作,读操作基本不会阻塞,性能提升明显。
- HashMap:在多线程环境下读操作性能可能受影响。虽然读操作本身不需要加锁,但如果发生扩容等操作,可能会影响读的一致性,并且当多个线程同时操作时,可能会导致数据竞争等问题,间接影响读性能。
- 写操作性能:
- ConcurrentHashMap:采用分段锁机制(Java 7 及之前版本)或 CAS 操作(Java 8 及之后版本),可以支持部分线程并行写操作,写操作的并发性能相对较高。例如在 Java 7 中,不同分段的写操作可以同时进行,不会相互阻塞。
- HashMap:在多线程环境下写操作需要额外的同步措施(如使用
Collections.synchronizedMap
进行包装),但这样会导致整个 map 被锁定,同一时间只能有一个线程进行写操作,性能较低。
差异原因
- 锁机制:
- ConcurrentHashMap:
- Java 7 及之前:采用分段锁(Segment)机制,将数据分成多个段,每个段有独立的锁。例如,默认有 16 个段,不同线程对不同段的操作可以并行,减少了锁竞争。
- Java 8 及之后:放弃了分段锁,采用 CAS 操作和 synchronized 关键字。在插入新元素时,首先使用 CAS 操作尝试插入,如果失败再使用 synchronized 锁住链表或红黑树的头节点,进一步提高了并发性能。
- HashMap:本身不是线程安全的,在多线程环境下如果要保证线程安全,需要外部同步,如使用
Collections.synchronizedMap
包装后,会对整个 map 加锁,导致所有读写操作都在同一把锁下进行,锁竞争激烈,性能降低。
- ConcurrentHashMap:
- 数据结构和操作方式:
- ConcurrentHashMap:在 Java 8 引入红黑树后,在处理哈希冲突时,当链表长度超过一定阈值会转换为红黑树,查询和插入性能在数据量较大且哈希冲突较多时更稳定。并且在并发操作时,对数据结构的调整采用更细粒度的方式,减少对其他线程的影响。
- HashMap:在处理哈希冲突时,主要使用链表,在哈希冲突严重时性能会下降。而且在多线程环境下进行扩容等操作时,由于数据结构调整涉及到整个 map,容易出现数据竞争和死循环等问题,为保证线程安全而采取的同步措施又会严重影响性能。