多线程不是让你写得更快,
是让你更快地写 Bug。
—— 所有并发程序员的共识
一、并发 ≠ 多线程,你要是说混了,面试官就开始写简历了
面试官:
并发和多线程有什么区别?
正确但不装逼的回答:
多线程是手段,并发是目标。
- 多线程:
JVM 里真的开了多个线程,同时(或看起来同时)跑代码 - 并发:
多个任务在同一时间段内推进,不必定真的同时执行
举个不抽象的例子:
- 单核 CPU + 多线程:
就像一个人同时炒菜、接电话、回微信
看起来很忙(并发),但一秒只能干一件事 - 多核 CPU + 多线程:
几个人一起做菜
这时候才是真的“同时”
面试加分句:
并发强调“任务管理”,并行强调“同时执行”。
二、创建线程的 4 种方式,真正有用的只有 2 种
面试官:
Java 创建线程有哪几种方式?
你可以这样答(真实开发版):
1️⃣ extends Thread
- 教科书写法
- 实际开发:基本不用
- 缘由:你把继承位用掉了,类就没法继承别的了
2️⃣ implements Runnable
- 最常用
- 线程和任务分离
- 符合面向对象
3️⃣ Callable + Future
- 需要返回值 / 抛异常时用
- 比 Runnable 更像“正经干活的线程”
4️⃣ 线程池(重点)
真正的生产环境:
你不 new Thread,你 new Thread 就是事故现场
三、为什么不能随意 new Thread?
面试官:
为什么推荐用线程池?
不要背“复用线程、降低开销”,换种说法:
new Thread 就像
每来一个请求,你就临时招一个员工
招完就开除
HR 累死,公司也快没了
线程池干了三件事:
- 控制线程数量(防止 CPU 被榨干)
- 复用线程(省钱)
- 统一管理任务(拒绝策略、队列、监控)
面试官最爱的一句话:
线程是有限资源,线程池是资源管理器。
四、volatile:它不是锁,但它很爱“打小报告”
面试官:
volatile 是怎么保证可见性的?
你可以这样说(不翻车版):
当一个线程修改 volatile 变量时,
JVM 会通过内存屏障,
强制把修改刷新到主内存,
其他线程读取时,必须从主内存拿最新值。
人话版比喻(准确版):
- 普通变量:
每个线程都有自己的“小本子”(CPU 缓存)
改了不必定告知别人 - volatile 变量:
改完必须贴到公司公告板(主内存)
其他人只能看公告板,不能看小本子
⚠️ 但重点来了:
volatile 不保证原子性
volatile int count = 0;
count++; // 依旧会翻车
五、synchronized 和 Lock 的区别(别背表格)
面试官:
synchronized 和 ReentrantLock 有什么区别?
可以这样答:
- synchronized:
JVM 原生锁
简单、可靠、不容易用错
自动加锁、自动释放 - ReentrantLock:
更“程序员友善”
可中断、可限时、公平锁
但你得自己记得 unlock()
一句话总结:
synchronized 像自动挡
ReentrantLock 像手动挡
老司机更爱手动挡,但新手容易熄火
六、死锁:不是线程坏,是你写得太自信
面试官:
什么是死锁?怎么避免?
死锁四个条件(不背版):
两个线程:
各自拿着一个锁
都不放
都在等对方先放
场面极其尴尬
如何避免?
- 统一加锁顺序
- 减少锁的粒度
- 使用 tryLock + 超时
- 能不用锁就不用锁(原子类 / 并发容器)
七、ThreadLocal:它不是共享,是“每人一份”
面试官:
ThreadLocal 是干嘛的?
不要说“线程本地变量”,换个说法:
ThreadLocal 的本质是:
以线程为 key 的 Map
使用场景:
- 用户信息
- TraceId
- 数据库连接
- 请求上下文
重点提醒(许多人翻车):
用了 ThreadLocal,
线程池里必定要 remove,
否则内存泄漏,神仙也救不了。
八、最后给你一句真实面试总结
并发问题 90% 不是 JVM 的锅,
是我们 太信任自己代码不会并发执行。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...




