🎤 高频真题
死锁 · 上下文切换 · 僵尸进程 · 多进程并发等经典考点
1. 死锁的四个必要条件?如何预防?
- 互斥:资源一次只能被一个进程使用(无法避免——锁的本质)
- 持有并等待:进程持有资源又在等别的资源
- 不可剥夺:资源只能主动释放
- 循环等待:存在等待环(P1 等 P2、P2 等 P1…)
预防策略(破坏任意一个非互斥条件):
- 破坏"持有并等待":一次性申请所有资源(难以实现)
- 破坏"不可剥夺":申请不到就释放已有资源(复杂)
- 破坏"循环等待"(最实用):资源排序,按固定顺序加锁——JVM 死锁排查章节讲的就是这个
🎯 面试要点
- 与并发模块呼应:MySQL 死锁、JVM 死锁都是同一理论
- 死锁检测 vs 预防:银行家算法(避免,靠安全序列)、资源分配图(检测环)
2. 什么是上下文切换?代价多大?
上下文切换:CPU 从一个线程/进程切到另一个——保存当前上下文(寄存器、程序计数器、栈指针、页表/缓存状态),加载新上下文。
代价:
- 直接开销:保存/恢复寄存器、内核态切换、调度器运行(微秒级)
- 间接开销:缓存失效(TLB、L1/L2 缓存清空,新进程重新加载)——这才是大头,切换越频繁吞吐越差
减少切换:线程数匹配 CPU 核数(线程池大小设置)、避免锁竞争(阻塞=切换)、协程(用户态切换,无内核开销)
🎯 面试要点
- vmstat 的 cs(context switch)列过高 = 切换频繁,配合线程池优化
- 为什么"线程不是越多越好":每个阻塞线程都在消耗切换成本
3. 僵尸进程和孤儿进程?
- 僵尸进程(Zombie):子进程已退出,但父进程没调用 wait() 回收——退出状态还在进程表中(占 PID,不占 CPU/内存)。父进程不回收会积累僵尸。
- 孤儿进程:父进程先退出,子进程被 init/systemd(PID 1)收养,由它回收——不会成僵尸
处理:查 ps aux | grep Z;僵尸的父进程是关键——修复父进程(回收逻辑)或 kill 父进程让 init 收养;kill 僵尸PID 无效(它已死)。
🎯 面试要点
- Java 中 Runtime.exec / ProcessBuilder 必须 waitFor/销毁,否则子进程僵死
- ps 状态列:R 运行、S 睡眠、D 不可中断(IO 等待)、Z 僵尸、T 停止
4. 其他高频考点速览?
- 银行家算法:死锁避免的经典——分配前检查是否安全序列(面试可能问思想)
- 信号量 vs 互斥锁:信号量是计数器(可 N 个资源,PV 操作);互斥锁是 0/1 特例
- 自旋锁 vs 互斥锁:自旋忙等(短临界区快,但耗 CPU);互斥睡眠(长临界区省 CPU)——JVM 轻量级锁自旋同理
- 惊群效应:多个进程同时等同一事件,唤醒全部但只有一个能用(浪费)→ epoll 的 EPOLLEXCLUSIVE、Nginx 锁解决
- 大端小端:字节序,网络序 = 大端;跨端通信要转换(htonl/ntohl)
- 局部性原理:时间局部性(刚用的还会用)+ 空间局部性(附近的会用)——缓存/预取/页缓存的理论基础
🎯 面试要点
- 局部性原理是贯穿性考点:CPU 缓存、PageCache、Redis、JVM 分层编译都能引用它
- 自旋 vs 互斥的选择就是"临界区长短"的权衡——与 synchronized 锁升级对照记忆