本文目录导读:

在Java案例复盘(尤其是面试复盘、项目复盘或线上故障复盘)中,提到的“技战术短板”通常指的是在解决具体业务问题时,因技术选型不当、编码质量不高或对底层原理理解不深,导致系统出现性能瓶颈、稳定性隐患或扩展性不足的问题。
为了帮你精准定位,我将这些短板分为四个维度(底层认知、并发编程、代码质量、架构设计),并结合具体的复盘案例场景来剖析:
底层原理与JVM认知浅薄(“内存”短板)
典型场景: 案例中出现OOM(内存溢出)、频繁Full GC(垃圾回收),或者死锁问题迟迟无法定位。
痛点短板:
- 对象逃逸与内存分配:只知
new对象,不知JVM的逃逸分析、TLAB(本地线程分配缓冲)机制,导致大量短命对象进入老年代,引发Major GC。 - 垃圾收集器选择:在面对大堆或低延迟需求时,仍然使用默认的PS+PO(Parallel Scavenge+Parallel Old)组合,未结合G1或ZGC的特性。
- 内存排查工具生疏:复盘时只开会讨论,不会用
jmap、jstat、MAT(内存分析工具)去定位是哪个业务线程导致的泄漏,只能靠重启解决(这是最典型的技战术短板)。
并发编程与锁竞争失控(“线程”短板)
典型场景: 线上突然出现接口响应变慢,CPU飙升(通常达到80%以上),但业务量并没有显著增长。
痛点短板:
- 锁粒度太粗:为保护一个局部变量使用了
Synchronized修饰整个方法,导致所有请求串行化,并发吞吐量急剧下降。 - 伪共享:在缓存行(通常为64字节)中,多个线程修改了相邻的变量,导致CPU缓存频繁失效,复盘时不了解
@Contended注解或不用LongAdder代替AtomicLong。 - 线程池策略错误:核心线程数设置过大(比如设为100)或队列用无界队列(如
LinkedBlockingQueue),导致高峰期线程全部阻塞,最终触发RejectedExecutionException(拒绝策略异常),复盘时往往只知道“任务积压”的表象,却找不出线程池参数与业务峰值的数学关系。
代码质量与接口设计(“IO”短板)
典型场景: 一个看似简单的查询接口,在数据量达到百万级后,响应时间从20ms飙升至3s以上。
痛点短板:
- N+1查询问题:在循环中单条查询数据库(如遍历用户列表查询每个用户的详细信息),这是新手案例中的常青树,复盘时发现没有使用批量查询(
IN)或没有通过关联查询一次性取出。 - 大事务与死锁:在
@Transactional注解内,既做了外部API调用(HTTP调用)又做了数据库更新,长事务导致数据库连接被长时间占用,且锁范围扩大引发死锁。 - 序列化隐患:使用了JDK自带的序列化(效率极低)或没有对超大JSON(JavaScript对象表示法)报文进行流式处理,导致内存中构建了巨大的字符串,增加了GC压力。
架构设计缺乏弹性(“架构”短板)
典型场景: 复盘时发现服务重启后,缓存被击穿,大量请求直接打穿到数据库,导致数据库连接池耗尽。
痛点短板:
- 没有防御性编程:未考虑缓存穿透(查询不存在的数据)、击穿(热点Key过期)、雪崩(缓存集中失效)的“三兄弟”问题,复盘时只怪Redis(远程字典服务),不怪自己的缓存Key过期时间设计不合理(如无随机抖动)。
- 接口幂等性缺失:在支付或回调接口中,未使用分布式锁或唯一ID约束,导致重复请求造成了数据重复。
- 依赖关系耦合:在代码中直接硬编码访问其他系统的配置(如IP地址、数据库密码),没有做服务降级或熔断(Sentinel/Hystrix),当依赖方宕机时,本地线程池被拖垮。
如何针对性弥补这些“技战术”短板?
- 建立事故复盘五问机制:每个问题必须追问到“JVM层面”或“IO层面”,杜绝“临时加了个缓存”这种表面解决,比如问:“加缓存后,缓存和数据库的一致性问题怎么解决?”
- 引入压测数据:复盘时必须附上JMeter或Arthas(阿尔萨斯大促诊断神器)的数据图,用实际CPU时间片和线程Dump(线程堆栈快照)来证明结论,而不是凭感觉。
- 代码审查清单化:将上述短板做成CheckList(检查清单),在Code Review(代码审查)时重点检查:是否没加索引、是否在循环中调RPC(远程过程调用)、是否用了“万能大对象”传参。
一句话总结:真正的短板不在于“不会用某个API”,而在于对并发模型的理解不深、对内存分布的不敏感以及缺乏全链路压测与监控的意识,在复盘时,如果你能主动说出“这里我用错了加锁策略,导致锁竞争激烈,应该用LongAdder拆分热点”,就证明技战术水平已经进阶了。