今天和大家分享一下组织内部成员在高级Java开发工程师岗位的面经详解,看看面试强度如何(删除了跟主人公项目相关的问题):
面经详解
1. 线程池参数怎么配置?拒绝策略?
线程池参数配置:
核心线程数(corePoolSize)
• CPU密集型任务:通常设置为CPU核心数 + 1
,例如4核CPU设置5。
• IO密集型任务:建议设置为CPU核心数 × 2
,例如4核CPU设置8,或通过公式CPU核心数 × (1 + 平均等待时间/计算时间)
动态调整。
• 获取CPU核心数:通过Runtime.getRuntime().availableProcessors()
获取。最大线程数(maxPoolSize)
• 根据任务突发流量和资源限制调整。IO密集型任务可设置为CPU核心数 × 2~4
,突发场景可更高。队列(workQueue)
• 有界队列(如ArrayBlockingQueue
):防止内存溢出,容量按需设置(如100~1000)。
• 无界队列(如LinkedBlockingQueue
):需谨慎使用,可能导致任务堆积。空闲线程存活时间(keepAliveTime)
• 非核心线程空闲超过此时间会被回收,通常设为60秒。
拒绝策略:
• AbortPolicy(默认):直接抛出异常,适用于需快速失败场景。
• CallerRunsPolicy:由提交任务的线程执行任务,用于降级处理。
• DiscardPolicy:静默丢弃新任务,可能导致数据丢失。
• DiscardOldestPolicy:丢弃队列中最老任务,尝试提交新任务。
2. IO密集型与CPU密集型任务配置
CPU密集型任务(如复杂计算、视频编码)
• 特点:高计算量、低IO等待、CPU使用率高。
• 线程池配置:核心线程数 ≈ CPU核心数,最大线程数等于核心线程数,避免过多线程导致上下文切换。IO密集型任务(如网络请求、文件读写)
• 特点:高IO等待时间、CPU空闲多。
• 线程池配置:核心线程数 ≈ CPU核心数 × 2,最大线程数可更高(如16),队列容量根据任务平均处理时间调整。
• 公式参考:线程数 = CPU核心数 × (1 + 平均等待时间/计算时间)
。
示例:
• 4核CPU的Web服务(IO密集型):核心线程数8,最大线程数16,队列容量200,拒绝策略 CallerRunsPolicy
。
3. 为什么消费消息和推送分开?
解耦与扩展性
• 消费消息负责处理业务逻辑(如订单支付),推送负责通知(如短信、App推送),解耦后两者可独立扩展。可靠性
• 推送失败时,消息仍保留在队列中,避免数据丢失。分开后消费服务无需关注推送的稳定性。流量控制
• 高并发场景下,分开可避免推送阻塞消费流程。例如,第三方推送接口QPS低时,推送服务可异步处理或降级。实时性优化
• 消费完成后立即返回结果,推送可延迟或批量处理(如合并多条通知)。
4. 如何优化索引?
索引选择
• 覆盖索引:查询字段全在索引中,避免回表。
• 联合索引:按最左前缀原则设计,例如(a, b, c)
可优化WHERE a=1 AND b=2
。避免无效索引
• 删除未使用或重复索引,减少写操作开销。查询优化
• 索引下推:在存储引擎层过滤数据,减少回表次数(如MySQL 5.6+)。
• 避免深分页:使用游标分页(记录上一页最后ID)替代LIMIT offset
。分库分表
• 数据量过大时,按业务垂直分库或按ID哈希水平分表。
5. 为什么不用openid和unionid联合分表?
数据分布不均
•openid
是用户唯一标识,而unionid
是同一主体下多应用的统一ID。联合分表可能导致数据倾斜(如同一企业用户集中在少数分表)。查询复杂度
• 联合分表需同时处理两个字段的路由逻辑,增加代码和维护成本。业务需求
• 若业务场景仅需按openid
查询(如用户订单),单字段分表更简单高效。
6. JVM如何优化?
堆内存设置
• 初始值(-Xms
)和最大值(-Xmx
)设为相同,避免动态调整(如-Xms4g -Xmx4g
)。垃圾回收器选择
• G1:适合大内存、低延迟场景(JDK9+默认)。
• Parallel GC:高吞吐量场景(如批处理)。新生代与老年代比例
• 通过-XX:NewRatio=2
设置新生代:老年代=1:2。监控工具
• 使用jstat
、VisualVM
分析GC日志,定位内存泄漏或频繁Full GC原因。
7. 多个RPC接口调用如何实现?
线程池异步调用
• 为每个RPC接口分配独立线程池,避免资源竞争。例如,支付接口和库存接口使用不同线程池。CompletableFuture编排
• 链式调用:使用thenApply()
、thenCompose()
串联任务。
• 并行调用:CompletableFuture.allOf()
等待多个接口完成。
8. 锯齿遍历二叉树(算法题)
思路:
- 层序遍历:使用队列按层遍历节点。
- 方向标记:偶数层反转结果(如第0层从左到右,第1层从右到左)。
代码:
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> ans = new ArrayList<>();
if (root == null) return ans;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
boolean isReverse = false;
while (!queue.isEmpty()) {
int size = queue.size();
List<Integer> level = new ArrayList<>();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
level.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
if (isReverse) Collections.reverse(level);
ans.add(level);
isReverse = !isReverse;
}
return ans;
}
9. 第三方SDK QPS低如何处理?
限流
• 令牌桶算法:控制请求速率(如GuavaRateLimiter
)。
• 队列缓冲:将请求暂存队列,异步处理。缓存
• 缓存高频请求结果,减少重复调用。异步调用
• 使用线程池或消息队列异步发送请求,避免阻塞主线程。备用方案
• 主备SDK切换(如腾讯云短信和阿里云短信)。
10. 低优先级消息如何进一步区分?
多级队列
• 按优先级分队列(如高、中、低),每个队列独立处理。动态权重
• 根据系统负载调整低优先级消息的处理权重(如空闲时多处理低优先级)。延迟处理
• 低优先级消息延迟消费(如Kafka消息设置delay
时间)。
11. ES的作用?
全文搜索
• 支持分词、模糊查询、高亮显示(如商品搜索)。日志分析
• 实时分析日志数据(如ELK栈)。数据分析
• 聚合统计(如用户行为分析)。
12. ES读和写的区别?
写入
• 近实时:数据写入后需refresh
(默认1秒)才可查。
• 分片策略:文档根据_routing
写入特定分片。读取
• 实时性:通过GET
可立即读取(因主分片直接返回)。
• 分布式查询:协调节点聚合各分片结果。
13. 现网问题如何处理?
监控告警
• 使用APM工具(如SkyWalking)监控CPU、内存、GC。日志分析
• 通过ELK分析异常日志,定位错误堆栈。回滚与扩容
• 紧急问题回滚至稳定版本,并发过高时扩容实例。限流降级
• 触发熔断(如Sentinel)或关闭非核心功能。
处理流程:
- 复现问题 → 2. 分析日志/监控 → 3. 定位代码 → 4. 修复验证 → 5. 灰度发布。
欢迎关注 ❤
我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。
没准能让你能刷到自己意向公司的最新面试题呢。
感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:面试群。
有疑问加站长微信联系(非本文作者))
