写点什么

不愧是高级 Java 开发岗,确实有点难~

作者:王中阳Go
  • 2025-04-01
    北京
  • 本文字数:2788 字

    阅读完需:约 9 分钟

不愧是高级Java开发岗,确实有点难~

今天和大家分享一下组织内部成员在高级 Java 开发工程师岗位的面经详解,看看面试强度如何(删除了跟主人公项目相关的问题):

面经详解

1. 线程池参数怎么配置?拒绝策略?

线程池参数配置:

  1. 核心线程数(corePoolSize)

  2. CPU 密集型任务:通常设置为 CPU核心数 + 1,例如 4 核 CPU 设置 5。

  3. IO 密集型任务:建议设置为 CPU核心数 × 2,例如 4 核 CPU 设置 8,或通过公式 CPU核心数 × (1 + 平均等待时间/计算时间) 动态调整。

  4. 获取 CPU 核心数:通过 Runtime.getRuntime().availableProcessors() 获取。

  5. 最大线程数(maxPoolSize)

  6. • 根据任务突发流量和资源限制调整。IO 密集型任务可设置为 CPU核心数 × 2~4,突发场景可更高。

  7. 队列(workQueue)

  8. 有界队列(如 ArrayBlockingQueue):防止内存溢出,容量按需设置(如 100~1000)。

  9. 无界队列(如 LinkedBlockingQueue):需谨慎使用,可能导致任务堆积。

  10. 空闲线程存活时间(keepAliveTime)

  11. • 非核心线程空闲超过此时间会被回收,通常设为 60 秒。

拒绝策略:

AbortPolicy(默认):直接抛出异常,适用于需快速失败场景。


CallerRunsPolicy:由提交任务的线程执行任务,用于降级处理。


DiscardPolicy:静默丢弃新任务,可能导致数据丢失。


DiscardOldestPolicy:丢弃队列中最老任务,尝试提交新任务。



2. IO 密集型与 CPU 密集型任务配置

  1. CPU 密集型任务(如复杂计算、视频编码)

  2. 特点:高计算量、低 IO 等待、CPU 使用率高。

  3. 线程池配置:核心线程数 ≈ CPU 核心数,最大线程数等于核心线程数,避免过多线程导致上下文切换。

  4. IO 密集型任务(如网络请求、文件读写)

  5. 特点:高 IO 等待时间、CPU 空闲多。

  6. 线程池配置:核心线程数 ≈ CPU 核心数 × 2,最大线程数可更高(如 16),队列容量根据任务平均处理时间调整。

  7. 公式参考线程数 = CPU核心数 × (1 + 平均等待时间/计算时间)


示例


• 4 核 CPU 的 Web 服务(IO 密集型):核心线程数 8,最大线程数 16,队列容量 200,拒绝策略 CallerRunsPolicy



3. 为什么消费消息和推送分开?

  1. 解耦与扩展性

  2. • 消费消息负责处理业务逻辑(如订单支付),推送负责通知(如短信、App 推送),解耦后两者可独立扩展。

  3. 可靠性

  4. • 推送失败时,消息仍保留在队列中,避免数据丢失。分开后消费服务无需关注推送的稳定性。

  5. 流量控制

  6. • 高并发场景下,分开可避免推送阻塞消费流程。例如,第三方推送接口 QPS 低时,推送服务可异步处理或降级。

  7. 实时性优化

  8. • 消费完成后立即返回结果,推送可延迟或批量处理(如合并多条通知)。



4. 如何优化索引?

  1. 索引选择

  2. 覆盖索引:查询字段全在索引中,避免回表。

  3. 联合索引:按最左前缀原则设计,例如 (a, b, c) 可优化 WHERE a=1 AND b=2

  4. 避免无效索引

  5. • 删除未使用或重复索引,减少写操作开销。

  6. 查询优化

  7. 索引下推:在存储引擎层过滤数据,减少回表次数(如 MySQL 5.6+)。

  8. 避免深分页:使用游标分页(记录上一页最后 ID)替代 LIMIT offset

  9. 分库分表

  10. • 数据量过大时,按业务垂直分库或按 ID 哈希水平分表。



5. 为什么不用 openid 和 unionid 联合分表?

  1. 数据分布不均

  2. openid 是用户唯一标识,而 unionid 是同一主体下多应用的统一 ID。联合分表可能导致数据倾斜(如同一企业用户集中在少数分表)。

  3. 查询复杂度

  4. • 联合分表需同时处理两个字段的路由逻辑,增加代码和维护成本。

  5. 业务需求

  6. • 若业务场景仅需按 openid 查询(如用户订单),单字段分表更简单高效。



6. JVM 如何优化?

  1. 堆内存设置

  2. • 初始值(-Xms)和最大值(-Xmx)设为相同,避免动态调整(如 -Xms4g -Xmx4g)。

  3. 垃圾回收器选择

  4. G1:适合大内存、低延迟场景(JDK9+默认)。

  5. Parallel GC:高吞吐量场景(如批处理)。

  6. 新生代与老年代比例

  7. • 通过 -XX:NewRatio=2 设置新生代:老年代=1:2。

  8. 监控工具

  9. • 使用 jstatVisualVM 分析 GC 日志,定位内存泄漏或频繁 Full GC 原因。



7. 多个 RPC 接口调用如何实现?

  1. 线程池异步调用

  2. • 为每个 RPC 接口分配独立线程池,避免资源竞争。例如,支付接口和库存接口使用不同线程池。

  3. CompletableFuture 编排

  4. 链式调用:使用 thenApply()thenCompose() 串联任务。

  5. 并行调用CompletableFuture.allOf() 等待多个接口完成。



8. 锯齿遍历二叉树(算法题)

思路


  1. 层序遍历:使用队列按层遍历节点。

  2. 方向标记:偶数层反转结果(如第 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 低如何处理?

  1. 限流

  2. 令牌桶算法:控制请求速率(如 Guava RateLimiter)。

  3. 队列缓冲:将请求暂存队列,异步处理。

  4. 缓存

  5. • 缓存高频请求结果,减少重复调用。

  6. 异步调用

  7. • 使用线程池或消息队列异步发送请求,避免阻塞主线程。

  8. 备用方案

  9. • 主备 SDK 切换(如腾讯云短信和阿里云短信)。



10. 低优先级消息如何进一步区分?

  1. 多级队列

  2. • 按优先级分队列(如高、中、低),每个队列独立处理。

  3. 动态权重

  4. • 根据系统负载调整低优先级消息的处理权重(如空闲时多处理低优先级)。

  5. 延迟处理

  6. • 低优先级消息延迟消费(如 Kafka 消息设置 delay 时间)。



11. ES 的作用?

  1. 全文搜索

  2. • 支持分词、模糊查询、高亮显示(如商品搜索)。

  3. 日志分析

  4. • 实时分析日志数据(如 ELK 栈)。

  5. 数据分析

  6. • 聚合统计(如用户行为分析)。



12. ES 读和写的区别?

  1. 写入

  2. 近实时:数据写入后需 refresh(默认 1 秒)才可查。

  3. 分片策略:文档根据 _routing 写入特定分片。

  4. 读取

  5. 实时性:通过 GET 可立即读取(因主分片直接返回)。

  6. 分布式查询:协调节点聚合各分片结果。



13. 现网问题如何处理?

  1. 监控告警

  2. • 使用 APM 工具(如 SkyWalking)监控 CPU、内存、GC。

  3. 日志分析

  4. • 通过 ELK 分析异常日志,定位错误堆栈。

  5. 回滚与扩容

  6. • 紧急问题回滚至稳定版本,并发过高时扩容实例。

  7. 限流降级

  8. • 触发熔断(如 Sentinel)或关闭非核心功能。


处理流程


  1. 复现问题 → 2. 分析日志/监控 → 3. 定位代码 → 4. 修复验证 → 5. 灰度发布。

欢迎关注 ❤

我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。


没准能让你能刷到自己意向公司的最新面试题呢。


感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:infoq 面试群。


发布于: 15 分钟前阅读数: 9
用户头像

王中阳Go

关注

靠敲代码在北京买房的程序员 2022-10-09 加入

【微信】wangzhongyang1993【公众号】程序员升职加薪之旅【成就】InfoQ专家博主👍掘金签约作者👍B站&掘金&CSDN&思否等全平台账号:王中阳Go

评论

发布
暂无评论
不愧是高级Java开发岗,确实有点难~_Java_王中阳Go_InfoQ写作社区