侧边栏壁纸
博主头像
5faith分享栈

憧憬未来

  • 累计撰写 9 篇文章
  • 累计创建 13 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

【面试题】Java部分[2025/1/6 ~ 2025/1/12]

faith5
2025-01-12 / 0 评论 / 0 点赞 / 15 阅读 / 0 字

1. 说说 Java 中 HashMap 的原理?

  • 基本说明
    HashMap是基于key-value对的集合类; jdk1.7使用数组+链表的方式, 而1.8使用数组+链表+红黑树的方式实现; 可以根据key快速查询; 是一个线程不安全的, 有一种情况: 在1.7使用的头插法在多线程下会可能产生回环的问题, 在1.8改为尾插法.
  • 扩容机制
    HashMap默认最大capacity(容量)是16, 负载因子(HashMap长度/capacity)默认是0.75, 默认情况下当HashMap的长度大于12的时候会出发自动扩容, 新的容量是旧容量的2倍;
  • 红黑树的引入
    由于1.8引入了红黑树, 在某些条件下链表会转表为数组:
    • 链表=>红黑树
      如果某个桶中的元素数量大于等于 8,且数组长度大于等于 64 时,链表转换为红黑树
    • 红黑树=>链表
      某个桶中的元素数量小于等于6时, 红黑树会转换为链表

2. Java 中 ConcurrentHashMap 1.7 和 1.8 之间有哪些区别?(461)

jdk1.7jdk1.8
锁机制使用分段锁(Segment),默认16个独立的Segment允许最多16个线程并发移除Segment,采用更细粒度的锁,在节点级别: 首次使用CAS操作,其余在对链表或红黑树的头节点使用synchronized。
数据结构数组+链表数组+链表+红黑树
扩容机制每个Segment是一个HashMap, 每个Segment都是独立扩容引入了一种新的扩容策略——渐进式扩容, 多个线程协作逐步进行, 会分批迁移到新数组中, 从而减少了扩容期间的性能开销; 在扩容过程中,使用了 CAS(Compare-And-Swap)操作来保证线程安全性,而不需要锁住整个数组
size计算方式尝试不加锁计算3次, 三次结果相同则返回当前值, 如果不同则会加锁遍历其他修改线程将会堵塞基于CountCell的数组,每个线程在自己对应的下标地方进行累加,等最后的时候把数组里面的数据统一获取最终的值

3. Java 中有哪些集合类?

image.png

4. 为什么 Java 8 移除了永久代(PermGen)并引入了元空间(Metaspace)

Java 8 移除永久代并引入元空间,主要是为了解决 PermGen 固定大小、容易导致内存溢出、GC 效率低的问题。元空间使用本地内存,具备更灵活的内存分配能力,提升了垃圾收集和内存管理的效率。

5. Java 线程池核心线程数在运行过程中能修改吗?如何修改?

  • 修改方式
    使用 ThreadPoolExecutor.setCorePoolSize(int corePoolSize) 方法可以动态修改核心线程数。corePoolSize 参数代表线程池中的核心线程数,当池中线程数量少于核心线程数时,会创建新的线程来处理任务。这个修改可以在线程池运行的过程中进行,立即生效。
  • 注意事项
    • 核心线程数的修改不会中断现有任务,新的核心线程数会在新任务到来时生效。
    • setCorePoolSize() 方法可以减少核心线程数,但如果当前线程池中的线程数量超过了新的核心线程数,多余的线程不会立即被销毁,直到这些线程空闲后被回收。

6. Java 中如何创建多线程? 简单 后端 Java Java并发

  1. 实现 Runnable 接口
  2. 继承Thread方法
  3. 使用Callable和FutureTask
  4. 使用线程池ExecuteService
  5. Completable(本质也是线程池,默认 forkjoinpool)
0

评论区