【JUC】大厂后端必问!Java 并发基础全套面试题详解(进程 / 协程 / 线程状态 / 锁机制)
·
大家好,我是程序员二叉。
简介
本文整理后端面试必考 Java 并发基础全集:进程与线程区别、Go协程与Java线程差异、线程三种创建方式优缺点、线程五大生命周期、守护线程机制、四大线程方法区别、wait/notify加锁原理、线程上下文切换原理,知识点全覆盖,可直接用于面试背诵。欢迎点赞关注收藏。
一、进程和线程的核心区别
1. 核心定义
- 进程:操作系统资源分配的最小单位
- 线程:CPU 调度执行的最小单位
2. 详细区别
- 资源隔离
- 进程:独立内存、资源完全隔离
- 线程:共享进程资源,仅私有栈、程序计数器
- 开销大小
- 进程:创建、切换、销毁 开销极大
- 线程:轻量级,开销极小
- 通信难度
- 进程:IPC通信(管道、共享内存、消息队列)复杂
- 线程:直接共享变量,通信简单
- 容错性
- 进程:相互独立,一个崩不影响其他
- 线程:一挂全挂,一个线程异常终止,整个进程退出
3. 一句话总结
进程是资源容器,线程是真正干活的执行单元。
二、Go协程(Goroutine)与Java线程区别
- 映射模型
- Java线程:1:1 内核线程,操作系统调度
- Go协程:M:N 用户态线程,Go runtime 调度
- 内存占用
- Java线程:默认栈 1MB
- Go协程:初始栈 2KB,动态伸缩
- 并发量级
- Java:上限几千线程
- Go:轻松 百万级协程
- 切换开销
- Java线程:内核态切换,开销大
- Go协程:用户态切换,无系统调用,极快
总结
Java线程是重量级系统线程,Go协程是超轻量级用户态执行单元。
三、Java线程三种创建方式 + 优缺点
1. 继承 Thread 类
- 优点:写法简单直观
- 缺点:Java单继承限制,无法继承其他类,扩展性差
2. 实现 Runnable 接口(推荐)
- 优点:规避单继承、适合资源共享、解耦
- 缺点:无返回值、不能抛异常
3. Callable + FutureTask
- 优点:有返回值、可捕获异常
- 缺点:代码相对繁琐
注意:调用
start()才是开启新线程,run()只是普通方法调用。
四、线程五大生命周期 & 流转
五大状态
- New 新建:创建线程对象,未启动
- Runnable 就绪:调用 start(),等待CPU时间片
- Running 运行:抢到CPU时间片,正在执行
- Blocked 阻塞:锁阻塞、等待唤醒、sleep、join
- Terminated 终止:线程执行结束/异常退出
状态流转
- New → start() → Runnable
- Runnable ↔ Running(CPU调度切换)
- Running → Blocked(sleep/join/wait/抢锁失败)
- Blocked → Runnable(唤醒、超时、获取锁成功)
- Running → Terminated
五、守护线程 & 用户线程区别
1. 定义
- 用户线程:业务工作线程,JVM 必须等全部用户线程结束才退出
- 守护线程:后台服务线程,用户线程全部结束,JVM直接退出,无视守护线程
2. 使用方式
thread.setDaemon(true); // 必须在 start() 之前设置
3. 应用场景
- GC 垃圾回收线程
- 后台心跳、监控、日志异步上报
- 闲置资源清理任务
六、sleep () /wait () /yield () /join () 核心区别
1. sleep()
- 属于 Thread 方法
- 不释放锁
- 限时休眠,时间到自动唤醒
- 进入 TIMED_WAITING
2. wait()
- 属于 Object 方法
- 释放锁
- 必须等待 notify/notifyAll 唤醒
- 进入 WAITING
3. yield()
- 属于 Thread 方法
- 不释放锁
- 主动让出 CPU,回到就绪态重新竞争
- 效果不稳定、极少使用
4. join()
- 属于 Thread 方法
- 当前线程等待目标线程执行完毕
- 底层基于 wait 实现
口诀
- sleep:抱着锁睡觉
- wait:释放锁等人喊
- yield:礼让 CPU
- join:等别人干完
七、为什么 wait/notify 必须放在 synchronized 中?
- 必须持有对象监视器锁
JVM 规范规定:执行 wait/notify 必须获取当前对象锁,否则直接抛出IllegalMonitorStateException。 - 防止唤醒丢失(核心)
不加锁会出现:条件判断通过瞬间,被其他线程修改,导致线程永久休眠、唤醒失效。 - 保证原子性
解决「判断 - 等待」并发竞态问题。
总结
wait/notify 是基于监视器锁的等待唤醒机制,必须同步保证安全。
八、线程上下文切换是什么?为什么耗性能?
1. 什么是上下文切换
CPU 从线程 A 切到线程 B:
- 保存当前线程上下文(栈、PC 寄存器、状态)
- 加载新线程上下文
该过程就是 线程上下文切换
2. 为什么耗时?
- 需要用户态切换内核态,系统开销大
- 频繁保存 / 恢复线程现场
- CPU 缓存失效,命中率下降
- 线程越多,切换越疯狂,CPU 利用率越低
结论
高并发不是线程越多越好,线程过多会导致频繁上下文切换,拖垮系统。
面试速记总结
- 进程是资源单位,线程是调度单位
- Go 协程用户态轻量、Java 线程内核态重量级
- 线程三种创建:Thread、Runnable、Callable
- 线程五状态:新建、就绪、运行、阻塞、终止
- 守护线程随 JVM 退出,用户线程必须执行完
sleep不释放锁、wait释放锁wait/notify必须加锁防止并发错乱- 上下文切换保存现场、消耗 CPU 资源
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)