Java面试题整理(附答案)
java
https://www.myquotego.com
在Java开发中,性能优化一直是让开发者头疼的问题,尤其是线上环境出现性能瓶颈时,更需要迅速定位问题、分析原因、提出解决方案。本篇文章,我将结合一次真实线上案例,带你从问题定位、技术原理解析,到代码优化实践,完整复盘整个性能优化过程。

1. 问题背景
在我们公司的电商系统中,最近上线了一个新功能------大促活动商品秒杀模块。功能上线后,用户反映系统偶尔出现响应缓慢甚至请求超时的情况,尤其在高并发场景下。
具体表现如下:
- 页面加载延迟:秒杀商品页面加载时间从正常的200ms增加到2秒以上。
- 接口响应超时:部分用户请求返回504 Gateway Timeout。
- CPU使用率异常:线上监控显示部分服务CPU长时间处于90%以上。
初步怀疑是数据库查询慢或Java代码存在性能瓶颈。
我整理了一套完整Java面试题库,
完整版在我的技术站:
https://www.myquotego.com/html/questions?_from=csdn_123_1
2. 技术原理解析
针对线上性能问题,我们需要从多维度分析:
2.1 Java线程池与并发控制
高并发环境下,如果线程池配置不合理,会导致请求堆积或者线程频繁创建销毁,从而增加CPU和GC压力。
- 核心线程数:建议根据CPU核数和任务性质(IO密集/CPU密集)设置。
- 队列长度:队列过长会增加等待时间,过短会频繁拒绝任务。
- 拒绝策略:可选择CallerRunsPolicy保证请求被执行而非直接丢弃。
java
ExecutorService executor = new ThreadPoolExecutor(
20, 100, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(200),
new ThreadPoolExecutor.CallerRunsPolicy()
);
2.2 数据库查询优化
数据库是性能瓶颈的高发地。常见问题:
- 慢查询:未使用索引或索引失效。
- N+1查询:循环中查询数据库。
- 连接池耗尽:高并发下连接池配置不足导致请求阻塞。
优化方法:
- 对查询频繁的字段建立复合索引。
- 使用批量查询减少循环查询。
- 调整连接池参数
maxActive,maxWait。
2.3 JVM内存与GC优化
频繁的Full GC会导致线程停顿,影响响应速度。分析工具:jstat, VisualVM, Flight Recorder。
常见问题:
- 对象创建过多:短生命周期对象占用Eden区频繁GC。
- 大对象直接进入老年代:增加Full GC压力。
解决思路:
- 对热点对象进行复用。
- 使用轻量级对象,减少包装类频繁创建。
- 调整堆内存分配,Eden/Tenured比例合理。
我整理了一套完整Java面试题库,
完整版在我的技术站:
https://www.myquotego.com/html/questions?_from=csdn_123_1
3. 代码示例
以下为我们实际优化的代码片段:
3.1 原始代码(存在性能问题)
java
public List<Product> getSeckillProducts(List<Long> productIds) {
List<Product> products = new ArrayList<>();
for (Long id : productIds) {
// 循环查询数据库,导致N+1问题
Product product = productMapper.selectById(id);
products.add(product);
}
return products;
}
问题分析:
- 每个ID都执行一次数据库查询。
- 高并发下,线程阻塞严重。
3.2 优化后的代码(批量查询+线程池并发)
java
public List<Product> getSeckillProducts(List<Long> productIds) throws InterruptedException {
// 使用批量查询
List<Product> products = productMapper.selectBatchIds(productIds);
// 并发处理库存计算
ExecutorService executor = Executors.newFixedThreadPool(20);
CountDownLatch latch = new CountDownLatch(products.size());
for (Product product : products) {
executor.submit(() -> {
try {
product.setAvailableStock(calculateStock(product));
} finally {
latch.countDown();
}
});
}
latch.await();
executor.shutdown();
return products;
}
优化效果:
- 数据库查询次数减少,解决N+1问题。
- CPU利用率合理,响应时间下降70%以上。
- 避免全局锁和阻塞,提高并发处理能力。
3.3 Redis缓存优化
对于热点商品数据,我们加入缓存:
java
public Product getProductFromCache(Long productId) {
String key = "product:" + productId;
Product product = redisTemplate.opsForValue().get(key);
if (product == null) {
product = productMapper.selectById(productId);
redisTemplate.opsForValue().set(key, product, 10, TimeUnit.MINUTES);
}
return product;
}
通过缓存:
- 大幅减少数据库压力。
- 高频请求直接命中缓存,提高响应速度。
我整理了一套完整Java面试题库,
完整版在我的技术站:
https://www.myquotego.com/html/questions?_from=csdn_123_1
4. 实际应用场景
- 电商秒杀系统:高并发抢购场景,需要数据库批量查询+缓存+线程池优化。
- 实时数据处理:金融行情、广告系统,CPU密集型计算可使用并发处理与对象复用。
- 接口响应优化:后台管理系统接口频繁访问数据库,缓存和批量查询可显著提升性能。
5. 总结
通过本次线上性能优化案例,我们得到几个经验:
- 线程池合理配置:结合任务类型和CPU核心数,避免线程饥饿或过度竞争。
- 数据库批量优化:减少循环查询,避免慢查询和N+1问题。
- 缓存策略:热点数据优先使用Redis缓存,降低数据库压力。
- JVM调优:关注对象创建和GC,减少Full GC停顿。
线上问题往往是多因素叠加导致的,性能优化需要综合考虑Java代码、数据库和缓存策略。
关注我,持续更新Java面试核心知识。