面试官:请简要介绍一下 Java 中的多线程,以及它在实际业务场景中的应用。
王铁牛:多线程就是一个程序里可以同时运行多个线程。在业务场景里,比如电商秒杀,多个用户同时抢商品,就可以用多线程来处理,提高效率。
面试官:那线程池呢?说说线程池的原理和优势。
王铁牛:线程池就是预先创建一些线程,当有任务来的时候,就从线程池里拿线程去执行任务。优势嘛,就是可以避免频繁创建和销毁线程,节省资源。
面试官:好,这一轮表现不错。接下来问几个关于 JVM 的问题。类加载机制分哪几个阶段?
王铁牛:嗯……好像是加载、验证、准备、解析、初始化。
面试官:那说说 JVM 内存模型都有哪些部分。
王铁牛:有堆、栈、方法区、程序计数器、本地方法栈。
面试官:最后一个,垃圾回收算法有哪些?
王铁牛:这个……我想想,有标记清除、标记整理、复制算法。
面试官:这一轮回答得还行。再进入下一轮,说说 HashMap 的底层数据结构。
王铁牛:HashMap 底层是数组加链表,后来链表长度超过 8 会转成红黑树。
面试官:那它是如何解决哈希冲突的?
王铁牛:就是通过链表或者红黑树来存储相同哈希值的元素。
面试官:当 HashMap 扩容时会发生什么?
王铁牛:扩容时会重新计算哈希值,然后重新分配元素到新的数组位置。
面试结束,面试官表示整体表现有亮点也有不足,复杂问题回答得不是特别清晰准确,让王铁牛回家等通知。
答案:
- 多线程:多线程是指在一个程序中可以同时运行多个线程。在实际业务场景中,比如电商秒杀,多个用户同时抢商品,就可以用多线程来处理,提高效率。多个线程可以同时执行不同的任务,互不干扰。每个线程都有自己的执行栈和程序计数器,它们共享进程的内存空间。
- 线程池:线程池是预先创建一些线程,当有任务来的时候,就从线程池里拿线程去执行任务。优势在于可以避免频繁创建和销毁线程,节省资源。线程池有核心线程池、阻塞队列、最大线程池等部分。当提交的任务数小于核心线程数时,核心线程会执行任务;当任务数大于核心线程数时,会将任务放入阻塞队列;如果阻塞队列满了,且线程数小于最大线程数,则会创建新线程执行任务;如果线程数达到最大线程数,任务会根据拒绝策略处理。
- JVM 类加载机制阶段:
- 加载:将类的字节码文件加载到内存中。
- 验证:检查字节码文件的格式是否正确,是否符合 JVM 规范等。
- 准备:为类的静态变量分配内存,并设置默认初始值。
- 解析:将符号引用转换为直接引用。
- 初始化:执行类的静态代码块,为静态变量赋真正的初始值。
- JVM 内存模型部分:
- 堆:是 JVM 中最大的一块内存区域,用于存储对象实例。
- 栈:每个线程都有自己的栈,用于存储局部变量、方法调用等。
- 方法区:存储类信息、常量、静态变量等。
- 程序计数器:记录当前线程执行的字节码指令地址。
- 本地方法栈:用于执行本地方法。
- 垃圾回收算法:
- 标记清除算法:先标记出所有需要回收的对象,然后统一回收。缺点是会产生大量不连续的内存碎片。
- 标记整理算法:先标记需要回收的对象,然后将存活对象向一端移动,最后清理边界以外的内存。
- 复制算法:将内存分为两块,每次只使用其中一块,当这块内存满了,就将存活对象复制到另一块,然后清理原来的那块。适用于存活对象少的场景。
- HashMap 底层数据结构:HashMap 底层是数组加链表,后来链表长度超过 8 会转成红黑树。数组的每个元素是一个链表节点(或红黑树节点)。
- HashMap 解决哈希冲突:当两个键的哈希值相同时,就会发生哈希冲突。HashMap 通过链表(长度超过 8 转红黑树)来存储相同哈希值的元素。新元素会添加到链表(或红黑树)的末尾。
- HashMap 扩容:当 HashMap 中的元素个数超过阈值(容量 * 负载因子)时,就会进行扩容。扩容时会创建一个新的更大的数组,然后重新计算每个元素的哈希值,将元素重新分配到新数组的相应位置。这个过程可能会导致链表或红黑树的结构发生变化。