2025最新场景题攻略-从 3 道 Java 场景题看实战能力:新手 vs 高手的解题差距在哪里?
·
作为 Java 开发者,面试时总躲不开 “场景题”—— 没有固定答案,却能瞬间暴露你的技术深度和工程思维。今天拆解 3 道高频场景题,看看新手和高手的解题思路有何不同,帮你避开踩坑点,快速提升实战能力。
Java场景题完整版:https://github.com/encode-studio-fe/natural_traffic/wiki/scan_material_yinke_0402
场景题 1:如何处理高并发下的库存扣减问题?
业务背景:秒杀系统中,10 万用户同时抢购 100 件商品,如何保证库存不超卖、不重复扣减?
新手思路:
直接在代码里写 “查询库存→判断库存> 0→扣减库存”,比如:
// 伪代码
int stock = selectStockById(goodsId);
if(stock > 0) {
updateStock(goodsId, stock - 1);
// 创建订单
}
❌ 问题:并发时多个线程同时查询到库存 > 0,导致超卖(比如库存 100,最终扣成 - 50)。
高手思路:
- 数据库层加锁:用
UPDATE ... WHERE原子操作,利用数据库行锁避免并发问题:
// 关键:更新时判断库存是否足够,原子操作
int affected = jdbc.update("UPDATE goods SET stock = stock - 1 WHERE id = ? AND stock > 0", goodsId);
if(affected > 0) {
// 扣减成功,创建订单
} else {
// 库存不足,返回失败
}
- 缓存预减库存:秒杀前把库存加载到 Redis,先扣 Redis 库存(用
decr命令,原子性),再异步同步到数据库,减少 DB 压力。 - 队列削峰:用 MQ 接收抢购请求,控制消费速度(比如每秒 1000 条),避免瞬间流量冲垮系统。
场景题 2:如何设计一个延迟任务系统?
业务背景:用户下单后 30 分钟未支付,自动取消订单;外卖下单后 10 分钟未接单,自动取消。
新手思路:
用Thread.sleep(30*60*1000)或定时任务Timer,到时间执行取消操作。❌ 问题:线程阻塞浪费资源,任务多了会 OOM;定时任务每隔 1 分钟轮询一次,存在延迟(比如 30 分钟的任务可能 31 分钟才执行)。
高手思路:
- 基于 Redis 的 ZSet 实现:
- 把任务 ID 和过期时间(时间戳)存入 ZSet,score 为过期时间。
- 启动一个线程,每隔 1 秒查询 ZSet 中
score < 当前时间戳的任务,执行后删除。
// 伪代码:添加延迟任务
redisTemplate.opsForZSet().add("delay_task", orderId, expireTime);
// 定时查询并执行
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(() -> {
long now = System.currentTimeMillis();
Set<String> taskIds = redisTemplate.opsForZSet().rangeByScore("delay_task", 0, now);
for (String taskId : taskIds) {
cancelOrder(taskId); // 执行取消订单
redisTemplate.opsForZSet().remove("delay_task", taskId);
}
}, 0, 1, TimeUnit.SECONDS);
- 成熟中间件:用 RabbitMQ 的延迟队列(TTL + 死信队列)或 RocketMQ 的延迟消息,更稳定可靠。
场景题 3:如何优化大文件上传(比如 1GB 视频)?
业务背景:用户上传大文件时,经常超时或失败,如何提升体验?
新手思路:
直接用MultipartFile接收文件,一次性上传。❌ 问题:文件太大导致内存溢出;网络波动易中断,中断后需重新上传。
高手思路:
- 分片上传:
- 前端将文件分成多个小块(比如 5MB / 块),每块带序号和文件唯一标识(如 MD5)。
- 后端接收分片,存储到临时目录,全部上传完成后合并。
// 伪代码:接收分片
@PostMapping("/uploadChunk")
public Result uploadChunk(@RequestParam("file") MultipartFile chunk,
@RequestParam("fileId") String fileId,
@RequestParam("chunkIndex") int index) {
// 保存分片到临时目录:/temp/{fileId}/{index}.part
chunk.transferTo(new File("/temp/" + fileId + "/" + index + ".part"));
return Result.success();
}
// 合并分片
@PostMapping("/mergeChunk")
public Result mergeChunk(@RequestParam("fileId") String fileId,
@RequestParam("totalChunks") int total) {
File targetFile = new File("/upload/" + fileId + ".mp4");
try (FileOutputStream out = new FileOutputStream(targetFile)) {
for (int i = 0; i < total; i++) {
File chunkFile = new File("/temp/" + fileId + "/" + i + ".part");
// 读取分片并写入目标文件
Files.copy(chunkFile.toPath(), out);
chunkFile.delete(); // 删除临时分片
}
}
return Result.success();
}
- 断点续传:前端上传前先查询已上传的分片序号,只传未上传的部分,减少重复传输。
- 并发上传:前端同时上传多个分片(控制并发数,比如 3 个),提升速度。
总结:高手解题的 3 个核心思维
- 原子性优先:涉及并发修改(如库存、订单状态),先用原子操作(数据库锁、Redis 命令)避免竞态条件。
- 资源效率:拒绝 “暴力等待”(sleep、轮询),用中间件(MQ、Redis)或异步机制优化资源占用。
- 分而治之:大任务拆分成小步骤(分片上传、分步提交),降低单次操作的复杂度和风险。
这些场景题是可以看出你是否真正理解 Java 技术的 “底层逻辑”。平时多思考 “如果是我设计,会如何避免踩坑?”,实战能力自然会提升~
Java场景题完整版汇总:https://github.com/encode-studio-fe/natural_traffic/wiki/scan_material_yinke_0402

更多推荐



所有评论(0)