作为 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)。

高手思路:

  1. 数据库层加锁:用UPDATE ... WHERE原子操作,利用数据库行锁避免并发问题:
// 关键:更新时判断库存是否足够,原子操作
int affected = jdbc.update("UPDATE goods SET stock = stock - 1 WHERE id = ? AND stock > 0", goodsId);
if(affected > 0) {
    // 扣减成功,创建订单
} else {
    // 库存不足,返回失败
}
  1. 缓存预减库存:秒杀前把库存加载到 Redis,先扣 Redis 库存(用decr命令,原子性),再异步同步到数据库,减少 DB 压力。
  2. 队列削峰:用 MQ 接收抢购请求,控制消费速度(比如每秒 1000 条),避免瞬间流量冲垮系统。

场景题 2:如何设计一个延迟任务系统?

业务背景:用户下单后 30 分钟未支付,自动取消订单;外卖下单后 10 分钟未接单,自动取消。

新手思路:

Thread.sleep(30*60*1000)或定时任务Timer,到时间执行取消操作。❌ 问题:线程阻塞浪费资源,任务多了会 OOM;定时任务每隔 1 分钟轮询一次,存在延迟(比如 30 分钟的任务可能 31 分钟才执行)。

高手思路:

  1. 基于 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);
  1. 成熟中间件:用 RabbitMQ 的延迟队列(TTL + 死信队列)或 RocketMQ 的延迟消息,更稳定可靠。

场景题 3:如何优化大文件上传(比如 1GB 视频)?

业务背景:用户上传大文件时,经常超时或失败,如何提升体验?

新手思路:

直接用MultipartFile接收文件,一次性上传。❌ 问题:文件太大导致内存溢出;网络波动易中断,中断后需重新上传。

高手思路:

  1. 分片上传
  • 前端将文件分成多个小块(比如 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();
}
  1. 断点续传:前端上传前先查询已上传的分片序号,只传未上传的部分,减少重复传输。
  2. 并发上传:前端同时上传多个分片(控制并发数,比如 3 个),提升速度。

总结:高手解题的 3 个核心思维

  1. 原子性优先:涉及并发修改(如库存、订单状态),先用原子操作(数据库锁、Redis 命令)避免竞态条件。
  2. 资源效率:拒绝 “暴力等待”(sleep、轮询),用中间件(MQ、Redis)或异步机制优化资源占用。
  3. 分而治之:大任务拆分成小步骤(分片上传、分步提交),降低单次操作的复杂度和风险。

这些场景题是可以看出你是否真正理解 Java 技术的 “底层逻辑”。平时多思考 “如果是我设计,会如何避免踩坑?”,实战能力自然会提升~

Java场景题完整版汇总:​​​​​​​https://github.com/encode-studio-fe/natural_traffic/wiki/scan_material_yinke_0402

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐