《Java面试85题图解版(二)》进阶深化上篇:并发编程 + JVM

📘 《Java面试85题图解版(二)》进阶深化上篇:并发编程 + JVM

阅读提示 :这是"图解+比喻+一句话总结"面试题库第二篇的进阶深化上篇 ,覆盖并发编程(7题)与JVM(7题)共14道面试高频题。每道题仍然是四层结构------结构图 → 场景比喻 → 关键对比表 → 一句话总结


一、并发编程(第30-36题)

📌 第30题:四种创建线程的方式

一图看清

复制代码
1. 继承Thread → 重写run()
2. 实现Runnable → 实现run()
3. 实现Callable → 实现call(),可返回结果,抛异常
4. 线程池(推荐) → 复用线程,控制资源
JDK21新增:虚拟线程 → 百万级并发,无需池化

比喻记忆请人干活

  • 继承Thread:指定某人是干活的料,直接让他干。
  • Runnable:写一张任务单,给谁都能执行。
  • Callable:任务单+回执,干完还给你结果。
  • 线程池:雇几个专职人员,来活了分配,干完回来待命。
  • 虚拟线程:临时工零成本,需要时直接雇,用完就走。

💡 一句话总结简单用Runnable,需返回值用Callable,生产必须线程池,JDK21可上虚拟线程。


📌 第31题:synchronized 锁升级过程

一图看清

复制代码
无锁 ─→ 偏向锁 ─→ 轻量级锁(自旋CAS) ─→ 重量级锁(OS Mutex)
        同一线程多次进    少量线程交替执行       激烈竞争,挂起唤醒

比喻记忆会议室使用规则

  • 无锁:会议室空着,谁都能用。
  • 偏向锁:小王经常用,门上贴"小王专用",直接进。
  • 轻量级锁:变成先到先得,小李偶尔来,门口徘徊等(自旋),抢到就进。
  • 重量级锁:人太多了,保安介入------"都别在门口转了,下来排队,叫号再进"。挂起唤醒慢。

💡 一句话总结无锁→偏向→轻量级→重量级,竞争越激烈锁越重,只升不降。


📌 第32题:volatile 的可见性与有序性

一图看清

复制代码
可见性 ✅:写后立即刷新主存,读前强制从主存取
有序性 ✅:禁止指令重排序(内存屏障)
原子性 ❌:i++ 复合操作仍不安全

比喻记忆大堂公告屏 vs 部门小黑板

  • 普通变量:各部门自己的小黑板。老板在财务部写了"销售额1亿",销售部看不见。
  • volatile:大堂LED公告屏,老板一写所有人都看见。但不能做"把总数+1"这种需要读+改+写三步的操作,三步中间别人也可能动。

💡 一句话总结volatile一写全公司可见,顺序不乱,但复合操作仍需加锁。


📌 第33题:synchronized vs ReentrantLock

一图看清

复制代码
synchronized:JVM内置,自动加解锁,非公平,单条件队列
ReentrantLock:API级,手动加解锁(try/finally),可公平,多Condition,可尝试加锁(tryLock)

比喻记忆电子门禁 vs 机械钥匙

  • synchronized:进门自动锁门,出门自动解锁。简单但功能少。
  • ReentrantLock:手动钥匙,可以公平排队,可以有多个等待区(Condition),可以试试"现在能不能开"(tryLock)。

💡 一句话总结简单场景synchronized够用;需要Condition/可中断/尝试加锁时用ReentrantLock。


📌 第34题:线程池核心参数与工作流程

一图看清

复制代码
新任务 →
 ├─ 核心线程未满 → 创建核心线程
 ├─ 核心线程满 → 入队列排队
 │    ├─ 队列未满 → 等待执行
 │    └─ 队列满 → 线程数<最大?→ 创建非核心线程
 │                         └─ 已达最大 → 拒绝策略
 └─ 空闲超时(keepAliveTime) → 非核心线程销毁

不推荐Executors:无界队列可能OOM,最大线程数可能Integer.MAX_VALUE

比喻记忆银行柜台

  • corePoolSize:5个常驻柜台,客户来了直接办。
  • workQueue:10个等候座位,坐满了经理才着急。
  • maximumPoolSize:临时开3个流动柜台,总共最多8个。
  • 拒绝策略:大厅站不下了,保安拦人"明天再来"。
  • keepAliveTime:高峰期过后,空闲临时柜台5分钟撤掉。

💡 一句话总结5常驻柜→10座等候→8临时极限→满了就拒,闲了撤临柜,绝不用Executors偷懒。


📌 第35题:ThreadLocal 原理与内存泄漏

一图看清

复制代码
Thread → ThreadLocalMap → Entry(key弱引用ThreadLocal → value强引用)
当外部强引用ThreadLocal置null → key被GC → Entry的value仍强引用 → 泄漏

线程池中线程长期复用 → ThreadLocalMap一直存在 → value无法回收

比喻记忆公司衣柜

  • Thread = 员工,ThreadLocalMap = 员工专属衣柜。
  • key弱引用 = 抽屉标签,撕掉后没人知道里面装啥。
  • value强引用 = 抽屉里的羽绒服,只要员工(线程)不走,羽绒服就占着柜子。
  • 线程池员工是长期工,衣柜越塞越满,必须每次用完清空------remove()

💡 一句话总结ThreadLocal的弱引用key被GC后value赖着不走,线程池必须手动remove。


📌 第36题:CompletableFuture 异步任务编排

一图看清

复制代码
supplyAsync → 启动异步任务,返回结果
thenApply → 转换结果
thenAccept → 消费结果,无返回
thenCombine → 合并两个独立任务的结果
thenCompose → 依赖上一步结果启动新任务(扁平化)
allOf / anyOf → 等所有完成 / 任一完成
exceptionally / handle → 异常兜底

比喻记忆餐厅取餐牌

  • supplyAsync:下单"宫保鸡丁",拿到取餐牌。
  • thenApply:菜好了自动加一份米饭(转换结果)。
  • thenCombine:鸡丁和鱼香肉丝同时做,好了放同一个托盘。
  • thenCompose:先看今日特价菜单,再决定点哪个(依赖第一步结果)。
  • allOf:宴请,所有菜齐了才开吃。
  • exceptionally:哪道菜做砸了,赔个水果盘。

💡 一句话总结CompletableFuture像取餐牌,能串行、并行、合并、兜底,告别Future.get()阻塞。


二、JVM(第37-43题)

📌 第37题:JVM 运行时数据区

一图看清

复制代码
线程共享:
  堆(对象实例、数组) | 方法区/元空间(类元信息、运行时常量池)
线程私有:
  程序计数器 | 虚拟机栈(栈帧:局部变量表、操作数栈) | 本地方法栈

比喻记忆公司办公大楼

  • :公共办公区,所有员工共用。保洁阿姨(GC)清理没人用的工位。
  • 方法区/元空间:公司档案室,保存组织架构、规章制度。JDK8后档案室搬到地下室(本地内存),不占主楼面积。
  • 虚拟机栈:员工个人工作记录本,任务完成翻回上页(方法返回)。
  • 程序计数器:手指指着当前正在执行哪一行代码。

💡 一句话总结堆和方法区大家共享,栈、PC寄存器、本地栈各线程独有。


📌 第38题:Java 堆的分代模型

一图看清

复制代码
新生代(Eden + S0 + S1,默认8:1:1)
  ↓ Minor GC存活后年龄+1 → 进入Survivor区
  ↓ 年龄达到阈值(默认15)→ 晋升老年代
老年代
  ↓ 大对象可直接进入

JDK8+:永久代→元空间(本地内存)
JDK9+:G1默认收集器,逻辑分代
JDK21:分代ZGC

比喻记忆公司员工分级

  • 新生代(Eden):试用期员工,大部分待不久就被优化。
  • S0/S1:转正考核区,每熬过一次考核年龄+1。
  • 老年代:老员工,熬过15次考核(或大对象直接空降高管)。
  • 元空间:公司规章制度,不占工位。

💡 一句话总结新生代复制回收,熬过去进老年代,元空间存类信息不占堆。


📌 第39题:垃圾回收算法

一图看清

复制代码
标记-清除:标记存活 → 清除未标记 → 产生内存碎片
复制算法:内存分两块 → 存活复制到另一块 → 清空当前块 → 浪费一半内存
标记-整理:标记存活 → 向一端移动 → 清边界外 → 无碎片但STW时间长
分代收集:新生代用复制,老年代用标记-清除/整理

比喻记忆三种搬家方式

  • 标记-清除:贴"要/不要"标签,不要的扔出窗外。房间空了但家具乱七八糟,大沙发放不下(内存碎片)。
  • 复制算法:你有A、B两间房,把A里还要的搬到B,一把火烧掉A。整齐但永远只用一半房间。
  • 标记-整理:把还要的推到房间一端,尾部全清空。最整齐但搬家最累(移动对象开销大)。

💡 一句话总结新生代复制(快),老年代整理(无碎片),分代组合是主流。


📌 第40-41题:常见垃圾回收器及CMS vs G1

一图看清

复制代码
Serial / Serial Old → 单线程,小内存Client模式
ParNew + CMS → 低延迟,并发标记清除,碎片+浮动垃圾,JDK8常用
Parallel Scavenge + Parallel Old → 关注吞吐量,后台计算
G1 → Region分区+可预测停顿,JDK9+默认
ZGC / Shenandoah → 超低延迟(<1ms),JDK21分代ZGC

比喻记忆清洁工团队

  • Serial:一个人干全部,小房间够用。
  • CMS:几个人边干活边让人用房间,低延迟但房间不整齐(碎片),偶尔要大扫除(退化为Serial Old全STW)。
  • G1:把大楼划分成多个区域,哪块最脏先清哪块,在规定时间内完成。JDK9后默认清洁团队。
  • ZGC:几乎看不见的清洁工,你感觉不到他们在工作。

💡 一句话总结低延迟CMS(老)→G1(JDK9+默认)→ZGC(JDK21分代,极低延迟)。


📌 第42题:类加载过程与双亲委派模型

一图看清

复制代码
类加载过程:加载 → 验证 → 准备 → 解析 → 初始化
             读取   格式   分配   符号   static
            字节码  检查   赋零值  引用→  赋值
                                 直接引用

双亲委派:请求向上委派(子→父→爷),加载向下查找(爷→父→子)
Bootstrap ClassLoader → Extension ClassLoader → Application ClassLoader → 自定义

比喻记忆新员工入职 + 小孩要零花钱

  • 加载:HR拿到简历,录入系统。
  • 验证:背调,检查学历证真伪、有无犯罪记录。
  • 准备:分配工位、电脑,但桌上还是空的(static变量赋零值)。
  • 解析:把"隔壁部门张三"这种称呼转成工号(符号引用→直接引用)。
  • 初始化:新员工到岗,摆个人物品,装软件(执行static赋值和static代码块)。

双亲委派:小孩要零花钱,先问爸,爸问爷。爷有就给,都没有爸爸才自己掏。防止小孩用假钞(自定义String)冒充真钱。

💡 一句话总结加载五步走,双亲委派向上请求向下加载,护住核心库不被篡改。


📌 第43题:OOM 排查与定位

一图看清

复制代码
常见OOM类型:
  Java heap space → 堆溢出,对象太多
  Metaspace → 元空间不够,类加载过多
  unable to create new native thread → 线程数超系统限制

排查工具链:
jstat → 监控GC和内存
jmap -dump → 导出堆快照
jstack → 看线程状态
MAT / JProfiler → 分析dump文件
GC日志 → -XX:+PrintGCDetails

比喻记忆查漏水

  • jstat + 监控:水管压力表,发现水位在涨。
  • jmap dump:拆开天花板,看哪段管子出问题。
  • MAT分析:工程师分析是哪个厂家的管道老化了。
  • 解决:换管(修代码)、调水压(调JVM参数)、加装蓄水池(扩容内存)。

💡 一句话总结监控+堆dump+MAT定位根源,修代码或调参,ThreadLocal和集合未清理是最常见泄漏点。


📌 下篇预告

本篇(上篇)我们覆盖了并发编程从锁升级到CompletableFutureJVM从运行时数据区到OOM排查的全部核心知识。这些正是面试官在中后段最爱追问的深水区。

下一篇(进阶深化中篇) 将进入 Spring核心与Spring Boot + 数据库进阶,涵盖IoC、AOP、循环依赖、自动装配、事务失效,以及MySQL与PostgreSQL的日志体系、索引类型、复制机制、分库分表等17道题。

👉 点击关注我进阶深化中篇更新后第一时间推送!

相关推荐
Mahir081 小时前
MySQL 数据一致性的基石:三大日志( redo log/undo log/binlog)与两阶段提交(Prepare 阶段和Commit 阶段)深度解密
数据库·后端·mysql·面试
abcnull1 小时前
用ASM做精准测试(Java)
java·jar·asm·字节码·精准测试
L0CK1 小时前
Redis 内存淘汰策略
后端
2501_931803751 小时前
Go:一门为解决C语言痛点而生的现代语言
c语言·开发语言·golang
zhengzizhe1 小时前
ReBAC 与 Google Zanzibar:权限系统的未来
后端·架构
用户8356290780511 小时前
使用 Python 自动创建 Excel 折线图
后端·python
梅兮昂2 小时前
Cloudflare Tunnel 实践教程
后端
@杰克成2 小时前
Java学习26
java·学习·idea
倒流时光三十年2 小时前
PostgreSQL VACUUM 清理机制详解
后端