面试题答案
一键面试性能瓶颈
- 锁粒度问题:Hashtable 内部使用 synchronized 关键字修饰方法,这意味着在高并发环境下,对 Hashtable 的任何操作(如 get、put 等)都会锁住整个 Hashtable 实例。当一个线程执行 put 操作时,其他线程即使只是想执行 get 操作,也必须等待锁的释放,大大降低了并发性能。例如,在一个有多个读操作和少量写操作的场景下,写操作的锁会阻塞大量读操作。
- 链表过长性能下降:Hashtable 的底层结构是数组加链表。当哈希冲突严重时,链表会变得很长。在查找元素时,时间复杂度会从理想的 O(1) 退化为 O(n),n 为链表长度。例如,在大量数据且哈希算法不够均匀的情况下,会频繁出现哈希冲突,导致链表过长,查询性能急剧下降。
优化思路
- 减小锁粒度 - 分段锁
- 实现方式:借鉴 ConcurrentHashMap 的设计思想,将 Hashtable 内部的数组进行分段,每个段都有独立的锁。当进行 put 或 get 操作时,只需要锁住对应的段,而不是整个 Hashtable。例如,将 Hashtable 的数组分为 16 段,每段有自己的锁对象。
- 收益:提高并发性能,多个线程可以同时对不同段进行操作,减少了锁竞争。在高并发读写场景下,系统的吞吐量会大幅提升。
- 风险:增加了代码复杂度,需要额外管理分段锁以及处理段与段之间的边界情况。同时,由于段的划分是固定的,如果数据分布不均匀,可能导致某些段的锁竞争依然激烈,而其他段的锁闲置。
- 优化哈希算法
- 实现方式:改进 Hashtable 内部的哈希算法,使其更加均匀地分布元素,减少哈希冲突。例如,可以采用更复杂的哈希函数,如 MurmurHash 等,它能在不同数据分布下,更均匀地将元素映射到哈希表的不同位置。
- 收益:降低链表长度,提高查找、插入和删除操作的性能。在高并发环境下,由于哈希冲突减少,锁竞争也会相应减少,进一步提升并发性能。
- 风险:复杂的哈希算法可能会增加计算开销,对于一些简单场景可能得不偿失。并且如果哈希算法的实现不当,可能会引入新的问题,如哈希碰撞的概率反而增加等。
- 使用红黑树代替链表
- 实现方式:当链表长度超过一定阈值(如 8)时,将链表转换为红黑树。在 JDK 8 之后的 HashMap 中就采用了这种优化策略。红黑树的查找、插入和删除操作平均时间复杂度为 O(log n),相比于链表的 O(n) 有显著提升。
- 收益:在哈希冲突严重时,极大提升了查找、插入和删除操作的性能,从而提升整个 Hashtable 在高并发环境下的性能表现。
- 风险:红黑树的实现相对链表更为复杂,需要额外的空间来存储树节点的一些属性(如颜色、父节点等),增加了内存开销。同时,红黑树的插入和删除操作可能会引发树的调整,这也会带来一定的性能开销。