面试题答案
一键面试底层数据结构
- Hashtable:基于哈希表数据结构,数组 + 链表实现,JDK1.8 之前和 HashMap 结构类似,但在解决哈希冲突时只使用链表。
- ConcurrentHashMap:JDK1.7 采用分段数组(Segment) + 链表结构,每个 Segment 继承自 ReentrantLock 实现锁分离;JDK1.8 借鉴了 HashMap 的设计,采用数组 + 链表 + 红黑树结构,锁粒度细化到每个桶(Node)。
同步机制
- Hashtable:对几乎所有涉及修改数据结构的方法(put、get、remove 等)都使用
synchronized
关键字进行同步,这意味着同一时间只有一个线程能访问 Hashtable,锁粒度大。 - ConcurrentHashMap:
- JDK1.7:通过分段锁(Segment)实现并发控制,每个 Segment 独立加锁,不同 Segment 可同时被不同线程访问,提高了并发度。
- JDK1.8:放弃了 Segment 分段锁机制,采用 CAS(Compare - and - Swap)和 synchronized 相结合的方式,对每个桶(Node)进行同步控制,锁粒度进一步减小,并发性能更好。
性能表现
- Hashtable:由于锁粒度大,在高并发场景下,线程竞争激烈,性能较差,所有线程都需要竞争同一把锁,导致大量线程等待。
- ConcurrentHashMap:
- JDK1.7:分段锁机制使得并发性能比 Hashtable 有很大提升,不同 Segment 上的操作可并行进行。
- JDK1.8:进一步优化了锁机制,锁粒度更小,并且引入红黑树优化哈希冲突,在高并发读写场景下性能比 JDK1.7 又有显著提升。
适用场景
- Hashtable:适用于单线程环境或并发度极低的场景,代码简单直接,不需要考虑复杂的并发问题。
- ConcurrentHashMap:适用于高并发场景,需要在保证线程安全的同时,尽可能提高并发读写性能。例如在多线程的 Web 应用中缓存数据、多线程计算任务中共享数据统计等场景。
优先选择 ConcurrentHashMap 的场景示例
假设有一个多线程的电商应用,多个线程会同时读取和更新商品库存信息。如果使用 Hashtable,在高并发时,大量线程会竞争同一把锁,导致性能瓶颈。而使用 ConcurrentHashMap,在 JDK1.8 中,不同线程可以对不同桶中的库存数据进行操作,减少了锁竞争,大大提高了并发性能。例如,线程 A 更新手机库存,线程 B 更新电脑库存,它们可以同时进行,无需等待对方释放锁。