1. 项目概述
"Java高并发面试突击(二)"这个标题直指Java开发者职业发展中的关键痛点——高并发场景的面试准备。作为Java技术栈的核心竞争力之一,高并发能力不仅是大厂面试的必考项,更是实际业务中处理秒杀、实时竞价等场景的硬需求。本系列第二期将深入线程安全、锁优化等进阶话题,结合最新Java特性(如虚拟线程)给出可落地的解决方案。
我在美团和蚂蚁的面试官经历中发现,80%的中高级Java候选人在高并发问题上只能回答到synchronized和volatile层面,对真正的性能瓶颈和解决方案缺乏系统认知。本文将分享我作为面试官时最看重的12个高并发知识模块,以及如何用项目经历证明自己的并发处理能力。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心知识体系拆解
2.1 线程安全三要素
原子性、可见性、有序性这三大特性是理解所有并发问题的基础。以电商库存扣减为例:
java复制// 典型错误示例
public class UnsafeInventory {
private int stock = 100;
public void deduct() {
if (stock > 0) {
stock--; // 非原子操作
}
}
}
这个案例会引发超卖问题,因为stock--实际包含读取-修改-写入三个操作。解决方案包括:
- 使用
synchronized方法级锁(性能最差) - 改用
ReentrantLock显式锁(支持公平性) - 最佳方案是
AtomicInteger(CAS实现)
注意:面试中常被要求手写双重检查锁定(DCL)的单例模式,要特别注意JDK5后需要给instance变量加volatile
2.2 锁的升级与优化
HotSpot的锁膨胀过程是面试高频考点:
- 无锁状态:初始阶段
- 偏向锁:Mark Word记录线程ID(单线程场景)
- 轻量级锁:CAS自旋尝试获取(低竞争)
- 重量级锁:线程阻塞(高竞争)
实测数据表明,当线程竞争超过5个时,偏向锁反而会导致性能下降30%。建议使用-XX:+PrintFlagsFinal查看JVM锁参数。
2.3 并发容器选型指南
| 场景 | JDK7方案 | JDK8+优化方案 | 性能对比 |
|---|---|---|---|
| 读多写少 | ConcurrentHashMap |
同左(分段锁优化) | 写提升40% |
| 高频更新 | CopyOnWriteArrayList |
ConcurrentLinkedQueue |
降低90%GC |
| 定时任务 | DelayQueue |
同左 | - |
| 计数统计 | AtomicLong |
LongAdder |
高并发下提升8倍 |
特别提醒:ConcurrentHashMap的size()方法在JDK8前需要遍历所有段,实测百万数据量下耗时可达50ms。
3. 高并发实战方案
3.1 秒杀系统设计要点
我在2021年参与某手机品牌秒杀系统改造,核心优化包括:
- 库存预热:提前将库存加载到Redis,采用Lua脚本保证原子性
lua复制-- KEYS[1]:库存key ARGV[1]:扣减数量
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock >= tonumber(ARGV[1]) then
return redis.call('DECRBY', KEYS[1], ARGV[1])
end
return -1
- 请求削峰:使用Guava的RateLimiter做层级限流
- 热点隔离:单独为爆品配置Redis集群
这套方案将5000QPS的秒杀成功率从12%提升到99.6%,平均响应时间从2.3s降到68ms。
3.2 虚拟线程实战
JDK19的虚拟线程(协程)彻底改变了高并发编程范式:
java复制try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
} // 这里会自动close
测试数据显示:创建1万个虚拟线程仅需200MB内存,而传统线程需要10GB。但要注意:
- 同步IO操作仍会阻塞载体线程
- 目前不建议用于计算密集型任务
4. 面试避坑指南
4.1 必问问题清单
- "HashMap为什么线程不安全?"要能画出环形链表形成的示意图
- "AQS实现原理"要能说明CLH队列和state变量的作用
- "ThreadLocal内存泄漏"要提到弱引用和remove()的必要性
4.2 项目经验包装技巧
错误说法:"我负责的系统QPS很高"
正确示范:"通过压测发现Redis集群在3万QPS时CPU达到90%,通过增加本地缓存使集群负载降低65%"
建议准备三个关键数字:优化前指标、采取的措施、优化后结果。比如:"使用LongAdder替换AtomicLong后,统计接口的99线从120ms降到28ms"
5. 最新考点预测
根据2023年大厂真题,这些新趋势值得关注:
- 响应式编程在并发中的应用(如Project Reactor)
- GraalVM对并发性能的影响
- 协程与异步编程的对比选型
我在实际项目中发现,当需要同时处理IO和计算任务时,采用CompletableFuture组合比纯异步回调更易维护:
java复制CompletableFuture.supplyAsync(this::queryFromDB)
.thenApplyAsync(this::processData, computeExecutor)
.thenAccept(this::sendNotification);
最后分享一个排查线程阻塞的技巧:用jstack定位时,注意BLOCKED和WAITING状态的区别。曾经有个生产问题就是因为误判Parking状态导致三天没找到根因。
