md_files/自学/JAVA面试题.md

3.0 KiB
Raw Blame History

JAVA面试题1

说说 Java 中 HashMap 的原理?

image-20250402170337830

为什么引入红黑树: 当hash冲突较多的时候链表中的元素会增多插入、删除、查询的效率会变低退化成O(n)使用红黑树可以优化插入、删除、查询的效率logN级别。

转换时机: 链表上的元素个数大于等于8 且 数组长度大于等于64 链表上的元素个数小于等于6的时候红黑树退化成链表。

链表插入方式变更:从"头插法"改为"尾插法"

  • 头插法特点

    • 插入时不需要遍历链表
    • 直接替换头结点
    • 扩容时会导致链表逆序
    • 多线程环境下可能产生死循环
  • 尾插法改进

    • 避免扩容时的链表逆序
    • 解决多线程环境下的潜在死循环问题

Java 中 ConcurrentHashMap 1.7 和 1.8 之间有哪些区别?

image-20250402170530218

image-20250402170553033

ConcurrentHashMap 不同JDK版本的实现对比

  1. 数据结构
  • JDK1.7

    • 使用 Segment分段锁 + HashEntry数组 + 链表 的数据结构
  • JDK1.8及之后:

    • 使用 数组 + 链表/红黑树 的数据结构与HashMap类似
  1. 锁的类型与宽度
  • JDK1.7

    • 分段锁(Segment)继承了 ReentrantLock
    • Segment容量默认16不会扩容 → 默认支持16个线程并发访问
  • JDK1.8

    • 使用 synchronized + CAS 保证线程安全
    • 空节点通过CAS添加put操作,多个线程可能同时想要将一个新的键值对插入到同一个桶中,这时它们会使用 CAS 来比较当前桶中的元素(或节点)是否已经被修改过。)
    • 非空节点通过synchronized加锁只锁住该桶,其他桶可以并行访问。
  1. 渐进式扩容JDK1.8+
  • 触发条件:元素数量 ≥ 数组容量 × 负载因子(默认0.75)
  • 扩容过程
    1. 创建2倍大小的新数组
    2. 线程操作数据时,逐步迁移旧数组数据到新数组
    3. 使用 transferIndex 标记迁移进度
    4. 直到旧数组数据完全迁移完成

500. 什么是 Java 的 CASCompare-And-Swap操作

CAS操作包含三个基本操作数

  1. 内存位置(V):要更新的变量
  2. 预期原值(A):认为变量当前应该具有的值
  3. 新值(B):想要更新为的值

CAS 工作原理:

  1. 读取内存位置V的当前值为A
  2. 计算新值B
  3. 当且仅当V的值等于A时将V的值设置为B
  4. 如果不等于A则操作失败(通常重试)
伪代码表示:
if (V == A) {
    V = B;
    return true;
} else {
    return false;
}