Java 并发与多线程面试题(真实面试版,不背书那种)

多线程不是让你写得更快,
是让你更快地写 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 累死,公司也快没了

线程池干了三件事:

  1. 控制线程数量(防止 CPU 被榨干)
  2. 复用线程(省钱)
  3. 统一管理任务(拒绝策略、队列、监控)

面试官最爱的一句话

线程是有限资源,线程池是资源管理器。


四、volatile:它不是锁,但它很爱“打小报告”

面试官:

volatile 是怎么保证可见性的?

你可以这样说(不翻车版):

当一个线程修改 volatile 变量时,
JVM 会通过内存屏障
强制把修改刷新到主内存
其他线程读取时,必须从主内存拿最新值

人话版比喻(准确版):

  • 普通变量:
    每个线程都有自己的“小本子”(CPU 缓存)
    改了不必定告知别人
  • volatile 变量:
    改完必须贴到公司公告板(主内存)
    其他人只能看公告板,不能看小本子

⚠️ 但重点来了:

volatile 不保证原子性

volatile int count = 0;
count++; // 依旧会翻车

五、synchronized 和 Lock 的区别(别背表格)

面试官:

synchronized 和 ReentrantLock 有什么区别?

可以这样答:

  • synchronized:
    JVM 原生锁
    简单、可靠、不容易用错
    自动加锁、自动释放
  • ReentrantLock:
    更“程序员友善”
    可中断、可限时、公平锁
    但你得自己记得 unlock()

一句话总结:

synchronized 像自动挡
ReentrantLock 像手动挡
老司机更爱手动挡,但新手容易熄火


六、死锁:不是线程坏,是你写得太自信

面试官:

什么是死锁?怎么避免?

死锁四个条件(不背版):

两个线程:

各自拿着一个锁

都不放

都在等对方先放

场面极其尴尬

如何避免?

  1. 统一加锁顺序
  2. 减少锁的粒度
  3. 使用 tryLock + 超时
  4. 能不用锁就不用锁(原子类 / 并发容器)

七、ThreadLocal:它不是共享,是“每人一份”

面试官:

ThreadLocal 是干嘛的?

不要说“线程本地变量”,换个说法:

ThreadLocal 的本质是:
以线程为 key 的 Map

使用场景:

  • 用户信息
  • TraceId
  • 数据库连接
  • 请求上下文

重点提醒(许多人翻车):

用了 ThreadLocal,
线程池里必定要 remove
否则内存泄漏,神仙也救不了。


八、最后给你一句真实面试总结

并发问题 90% 不是 JVM 的锅,
是我们 太信任自己代码不会并发执行。

© 版权声明

相关文章

暂无评论

none
暂无评论...