JUC 实际业务高频面试题浅谈

JUC 实际业务高频面试题浅谈(由浅入深+底层原理+业务落地)

整体学习顺序:线程基础 → 并发三大特性/JMM → 内置锁synchronized → 显式锁Lock/AQS → 同步工具类 → 线程池 → 并发容器 → CompletableFuture异步编排 → ThreadLocal → 真实业务并发场景实战


一、Java线程基础(JUC前置必问)

1. 进程和线程的区别?业务中为什么要用多线程?

面试答案

  • 进程:资源分配最小单位,独立内存空间,进程间隔离性强、通信成本高;
  • 线程:调度执行最小单位,共享进程堆/方法区,只独有栈/程序计数器,切换成本极低;
  • 多线程适用:IO密集型(接口调用、文件读写、数据库查询)、批量任务处理、异步解耦、多任务并行;不适用:CPU密集型且计算简单(线程切换反而损耗性能)。

底层原理

线程是轻量级进程,OS内核调度,Java线程1:1映射系统内核线程。

业务场景

电商批量订单推送、批量导入Excel、微服务多接口并行查询、日志异步打印。

2. 线程有哪些生命周期?状态如何流转?

6大状态 :NEW新建 → RUNNABLE就绪运行 → BLOCKED阻塞 → WAITING无限等待 → TIMED_WAITING限时等待 → TERMINATED终止。
流转核心

  • 抢锁失败→BLOCKED;
  • wait()/join()/LockSupport.park()→WAITING;
  • sleep()/wait(time)→TIMED_WAITING。

业务落地

排查线上线程阻塞、死锁、接口超时,必看线程堆栈状态。

3. start() 和 run() 有什么区别?为什么不能直接调用run()?

  • start():向OS申请创建内核线程,由JVM调度执行run()真正开启新线程
  • 直接调用run():只是普通方法调用,仍在当前主线程执行,无多线程效果。

二、并发三大特性 & JMM内存模型(JUC核心基石)

1. 并发三大特性是什么?JMM如何保障?

三大特性

  • 原子性:操作不可分割,要么全执行要么不执行;
  • 可见性:一个线程修改共享变量,其他线程立刻感知;
  • 有序性:程序按代码顺序执行,禁止指令重排序。

JMM保障方案

  • 原子性:synchronized、Lock、Atomic原子类;
  • 可见性:volatile、synchronized、final;
  • 有序性:volatile 禁止重排序、内存屏障。

2. volatile 关键字作用?能保证原子性吗?底层原理?

面试答案

  1. 保证可见性有序性不保证原子性
  2. 禁止指令重排序,适用于状态标记、单例DCL。

底层原理

volatile 底层通过 内存屏障 + 缓存一致性协议(MESI)实现:

  • 写屏障:强制刷新工作缓存到主内存;
  • 读屏障:强制从主内存加载最新数据。

为什么不能保证原子性
i++读-改-写三步操作,volatile 只能保证每次读到最新值,无法锁住三步操作,多线程下仍会覆盖。

业务场景

  • 秒杀系统标记秒杀结束状态;
  • 服务启停开关、配置动态刷新标记;
  • DCL双重检查锁单例。

3. 什么是指令重排序?DCL单例为什么必须加volatile?

指令重排序:CPU/JVM为了优化执行效率,打乱代码执行顺序,单线程无影响,多线程会出bug。

DCL漏洞原因
new 对象 底层三步:分配内存→初始化对象→赋值引用;

重排序后可能变成:分配内存→赋值引用→初始化对象,其他线程拿到半初始化空对象 ,引发空指针。
volatile 禁止构造方法和赋值之间重排序,彻底解决该问题。


三、synchronized 内置锁(面试必问,业务最常用)

1. synchronized 三种用法及区别?

  1. 修饰实例方法:锁当前对象this;
  2. 修饰静态方法:锁类对象Class,全局唯一;
  3. 修饰代码块:锁自定义对象,粒度更细。

业务选型

优先用代码块锁,缩小锁粒度,提升并发性能。

2. synchronized 底层锁升级原理?偏向锁/轻量级/重量级

锁升级顺序偏向锁 → 轻量级锁(自旋)→ 重量级锁只能升级不能降级

  1. 偏向锁:单线程无竞争,标记线程ID,无加锁开销;
  2. 轻量级锁:轻微竞争,CAS自旋抢锁,不阻塞内核;
  3. 重量级锁:竞争激烈,自旋失败,进入OS内核阻塞,开销最大。

原理核心

锁信息存在 对象头MarkWord 中,JDK1.6之后自适应自旋、锁优化大幅降低开销。

业务优化

尽量减少锁竞争、缩小锁粒度,避免直接升级为重量级锁导致接口卡顿。

3. synchronized 和 ReentrantLock 区别?业务怎么选型?

特性 synchronized ReentrantLock
实现 JVM底层原生 基于AQS代码实现
可中断 不可中断 可中断lockInterruptibly
超时抢锁 tryLock(time) 超时放弃
公平锁 非公平默认 支持公平/非公平
条件队列 1个等待队列 多个Condition条件队列
自动释放 自动释放 必须finally手动unlock

业务选型

  • 简单同步、代码简洁:用 synchronized
  • 需要超时抢锁、可中断、读写分离、多条件等待:用 ReentrantLock

四、Lock & AQS 核心原理(JUC基石,进阶必问)

1. ReentrantLock 可重入原理?公平锁和非公平锁业务区别

可重入原理

AQS 有 state 状态变量:

  • 加锁:state+1;
  • 重入:再次加锁state继续+1;
  • 释放锁:state-1,减到0才真正释放。

公平锁vs非公平锁

  • 非公平锁:上来直接CAS抢锁,不排队,吞吐量高,默认使用;
  • 公平锁:必须排队按顺序抢锁,避免线程饥饿,吞吐量低。

业务场景

  • 高并发秒杀、接口限流:非公平锁;
  • 订单排队处理、需保证先后顺序:公平锁。

2. AQS 是什么?核心设计思想

AQS(抽象队列同步器)

JUC所有锁/工具类的底层基石,核心:

  1. volatile int state:同步状态(锁标记、资源计数);
  2. 双向CLH阻塞队列:抢锁失败的线程入队阻塞;
  3. 模板方法:自定义锁只需实现 tryAcquire/tryRelease

基于AQS的组件

ReentrantLock、ReadWriteLock、CountDownLatch、Semaphore、CyclicBarrier、线程池。

3. ReadWriteLock 读写锁原理&业务场景

原理

读锁共享、写锁独占:

  • 多线程可以同时加读锁;
  • 有读锁时写锁阻塞,有写锁时读写都阻塞。

业务场景
读多写少 场景:本地缓存配置、商品基础信息、字典数据、系统参数。

极高提升并发读性能,比独占锁强很多。


五、三大同步工具类(业务高频使用)

1. CountDownLatch、CyclicBarrier、Semaphore 区别及业务场景

(1)CountDownLatch 倒计时门闩

原理 :初始化计数器,线程调用countDown()-1,await()等待计数器归0才放行;一次性使用,不能重置
业务场景

  • 微服务多接口并行查询,所有接口返回后再汇总结果;
  • 批量多线程处理任务,等待所有子线程执行完毕再主线程收尾。
(2)CyclicBarrier 循环栅栏

原理 :一组线程互相等待,凑齐指定数量同时放行;可循环重复使用
业务场景

  • 多人协作任务,所有人准备好再同时开始;
  • 分批次批量处理数据,每批凑齐线程再执行。
(3)Semaphore 信号量

原理 :控制同时访问资源的线程数量,做限流 ;acquire获取许可,release释放许可。
业务场景

  • 接口限流、第三方接口调用限流、数据库连接池控制并发;
  • 秒杀系统限制瞬时请求并发数。

六、线程池(面试重中之重,业务必用)

1. 为什么禁止用 Executors 创建线程池?

  • FixedThreadPool/ SingleThreadPool:无界队列,任务过多堆积引发OOM;
  • CachedThreadPool:最大线程无上限,无限创建线程,耗尽CPU内存。

业务规范

必须通过 ThreadPoolExecutor 手动指定7大参数,自定义线程池。

2. 线程池七大核心参数详解

  1. corePoolSize:核心线程数(常驻不回收)
  2. maximumPoolSize:最大线程数
  3. keepAliveTime:空闲线程超时时间
  4. unit:时间单位
  5. workQueue:任务阻塞队列
  6. threadFactory:线程工厂(自定义线程名,便于排查)
  7. handler:拒绝策略

3. 核心线程数怎么配置?CPU密集/IO密集

公式

  • CPU密集型(计算多):核心线程数 = CPU核心数 + 1
  • IO密集型(数据库/接口等待多):核心线程数 = CPU核心数 * 2 ~ 2倍

业务实战

业务系统90%都是IO密集型,直接设为CPU核心数2倍即可。

4. 四种拒绝策略原理及业务适用

  1. AbortPolicy:直接抛异常(默认)→ 核心业务,不允许丢任务;
  2. CallerRunsPolicy:主线程自己执行→ 非核心业务,不抛异常不丢任务;
  3. DiscardPolicy:直接丢弃新任务→ 允许丢失的日志、统计任务;
  4. DiscardOldestPolicy:丢弃队列最旧任务→ 秒杀排队、订单排队。

5. 线程池执行流程

  1. 来了任务,核心线程数未满 → 创建核心线程执行;
  2. 核心线程已满 → 任务进入阻塞队列;
  3. 队列已满 → 创建非核心线程直到最大线程数;
  4. 达到最大线程数+队列满 → 触发拒绝策略。

6. 业务中线程池如何避免OOM和内存泄漏

  1. 自定义有界队列,禁止无界队列;
  2. 合理设置最大线程数,不超限;
  3. 使用自定义线程工厂,设置线程名、守护线程;
  4. 任务内部捕获异常,避免线程莫名退出;
  5. 定时监控线程池活跃线程、队列堆积数量。

七、JUC并发容器(替代普通集合线程不安全)

1. ArrayList 为什么线程不安全?

多线程同时add时,会出现数组越界、元素覆盖、数据丢失,底层无任何同步控制。

2. ConcurrentHashMap JDK7和JDK8底层原理

JDK7 :分段锁Segment,分成16个分段,每个分段独立加锁,并发度16;
JDK8 :取消分段锁,数组+链表+红黑树CAS+synchronized 锁链表头节点,粒度更细、并发更高。

业务场景

全局本地缓存、配置存储、高并发键值存储,优先使用ConcurrentHashMap。

3. CopyOnWriteArrayList 原理&适用场景

原理 :写时复制,修改时复制新数组,修改完替换原数组;读无锁、写加锁
特点 :读性能极高,写性能差,占用内存。
业务场景读多写少,白名单、配置列表、权限角色列表。

4. BlockingQueue 阻塞队列业务用途

核心:生产者消费者模型

业务落地:本地消息缓冲、订单异步处理、日志队列、秒杀请求排队。


八、CompletableFuture(业务异步编排必问)

1. CompletableFuture 相比于 Future 优势

  • Future 只能阻塞get()获取结果,无法回调、无法编排多任务;
  • CompletableFuture 支持异步回调、串行执行、并行组合、异常处理,是业务异步首选。

2. 常用核心API业务场景

  1. supplyAsync/runAsync:开启异步任务;
  2. thenApply/thenAccept:任务串行依赖;
  3. thenCombine/allOf:多任务并行执行;
  4. exceptionally:异步任务异常兜底。

真实业务

电商订单详情页:同时查用户信息、商品信息、库存、优惠券,并行调用,把接口响应时间从500ms降到150ms。


九、ThreadLocal 原理&业务深坑

1. ThreadLocal 底层原理

每个Thread内部有 ThreadLocalMap ,key是ThreadLocal(弱引用),value是存储的业务对象。
特点:线程私有,数据隔离,线程间互不干扰。

2. ThreadLocal 为什么会内存泄漏?怎么解决?

泄漏原因

key是弱引用会被GC回收,但value是强引用,线程不销毁(线程池),value永远无法回收。

解决方案

使用完必须手动调用 remove()

业务拦截器结尾统一清理ThreadLocal。

3. 业务使用场景

  • 用户登录上下文透传(不用每一层接口传用户ID);
  • 权限、租户ID全局透传;
  • 事务上下文、分页参数全局封装。

十、真实业务经典并发综合面试题

1. 秒杀系统如何用JUC防超卖+限流?

  1. 限流:Semaphore 控制瞬时并发;
  2. 防超卖:分布式锁Redisson + 本地ReentrantLock 双层锁;
  3. 库存标记:volatile 标记秒杀结束;
  4. 异步削峰:BlockingQueue 排队处理秒杀请求。

2. 批量导入几十万数据怎么用JUC优化?

线程池 + CountDownLatch 拆分分片,多线程并行入库,执行完毕主线程返回结果,比单线程快数倍。

3. 本地配置缓存怎么优化读写性能?

ReadWriteLock 读写锁,读多共享、写独占,兼顾并发读安全和性能。

4. 微服务多接口聚合查询怎么优化RT?

用 CompletableFuture.allOf 并行调用多个远程接口,等待全部完成后组装返回,大幅降低接口耗时。

相关推荐
初圣魔门首席弟子1 小时前
bug 2026.05.15(以前能运行的java springboot项目突然间不能运行后台数据了)
java·开发语言·bug
古怪今人1 小时前
项目和模块 一个目录下创建多个项目 IDEA Multi-Project Workspace插件
java·ide·intellij-idea
小英雄大肚腩丶1 小时前
RabbitMQ消息队列
java·数据结构·spring boot·分布式·rabbitmq·java-rabbitmq
fengxin_rou2 小时前
用户模块架构实战:DTO 与 Domain 分层、Optional 空值处理、事务只读优化详解
java·后端·架构·用户实战
redaijufeng2 小时前
C++构造函数详解:从基础原理到实际应用
java·jvm·c++
yuzhiboyouye3 小时前
VO一般java后端怎么转换成前端想要的数据
java·前端·状态模式
一 乐3 小时前
学院教学工作量统计|基于java+ vue学院教学工作量统计管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·学院教学工作量统计系统
迷藏4943 小时前
【无标题】
java·数据库·oracle
河阿里3 小时前
Java包装类(Wrapper):自动装箱拆箱机制与类型转换的那些坑
java·开发语言