企业面试题综合(2)

1. CompletableFuture组合异步任务的异常处理

异常处理方法

  1. exceptionally

    • 处理链中任意阶段的异常,返回默认值。
    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
        if (Math.random() > 0.5) throw new RuntimeException("Error");
        return "Success";
    }).exceptionally(ex -> {
        System.err.println("Exception: " + ex.getMessage());
        return "Default";
    });
    
  2. handle

    • 同时处理正常结果和异常,支持恢复逻辑。
    future.handle((result, ex) -> {
        if (ex != null) {
            return "Recovered";
        }
        return result;
    });
    
  3. whenComplete

    • 执行清理操作,但不修改结果。
    future.whenComplete((res, ex) -> {
        if (ex != null) {
            System.err.println("Task failed: " + ex.getMessage());
        }
    });
    
  4. 多任务异常处理

    • 使用 allOfanyOf 统一处理多个任务异常。
    CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2);
    allFutures.exceptionally(ex -> {
        System.err.println("All tasks failed: " + ex.getMessage());
        return null;
    });
    

2. CompletableFuture异步任务超时处理

解决方案

  1. get(timeout, TimeUnit)

    • 直接设置超时时间,超时后抛出异常。
    try {
        String result = future.get(5, TimeUnit.SECONDS);
    } catch (TimeoutException e) {
        future.cancel(true); // 取消任务
    }
    
  2. 自定义超时逻辑

    • 结合 CompletableFutureScheduledExecutorService
    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    CompletableFuture<String> timeoutFuture = new CompletableFuture<>();
    scheduler.schedule(() -> timeoutFuture.completeExceptionally(new TimeoutException()), 5, TimeUnit.SECONDS);
    CompletableFuture.anyOf(future, timeoutFuture).thenApply(result -> {
        if (timeoutFuture.isDone()) {
            System.err.println("Task timed out");
        }
        return result;
    });
    

3. JMM(Java内存模型)

  • 核心作用
    • 定义线程如何与主内存交互,确保可见性、原子性和有序性。
  • 关键规则
    1. happens-before:定义操作顺序,如 synchronizedvolatile 写-读。
    2. 可见性volatile 确保变量修改对其他线程立即可见。
    3. 原子性synchronizedAtomicXXX 类保证复合操作的原子性。
    4. 有序性:禁止指令重排序(通过内存屏障)。

4. JVM内存模型

内存区域划分

  1. 堆(Heap)
    • 存放对象实例,分为新生代(Young)和老年代(Old)。
  2. 栈(Stack)
    • 存储局部变量和方法调用。
  3. 方法区(Metaspace)
    • 存储类元数据(如类定义、常量池)。
  4. 程序计数器
    • 记录当前线程执行的字节码地址。

5. 方法重载 vs 方法重写

特性方法重载(Overload)方法重写(Override)
定义位置同一个类或子类子类覆盖父类方法
方法签名方法名相同,参数列表不同方法名、参数列表、返回类型相同
访问权限可以更严格不能更严格(如父类是 protected,子类不能是 private
目的提供多种调用方式实现多态

6. Redis持久化机制

RDB(快照)

  • 优点
    • 文件体积小,恢复速度快。
    • 不阻塞主线程(通过子进程生成快照)。
  • 缺点
    • 数据可能丢失(最后一次快照后未持久化的数据)。
    • 不适合高频率写入场景。

AOF(Append-Only File)

  • 优点
    • 数据安全性高(每条写命令记录)。
    • 支持重写压缩(BGREWRITEAOF)。
  • 缺点
    • 文件体积大,恢复速度慢。
    • 频繁写入可能影响性能。

部署方式

  1. 单机部署:适用于小型应用,简单易用。
  2. 主从复制:主节点写,从节点读,提升读性能。
  3. 集群部署:分片存储,高可用,支持水平扩展。

7. 查询全表性能差的优化

  1. 添加索引

    • 在查询条件字段(如手机号)上创建索引。
    CREATE INDEX idx_phone ON users(phone);
    
  2. 分区表

    • 按手机号范围或哈希分区,减少扫描数据量。
    CREATE TABLE users_partitioned (
        id INT,
        phone VARCHAR(20)
    ) PARTITION BY RANGE (id);
    
  3. 缓存

    • 使用Redis缓存高频查询结果(如手机号对应的用户ID)。
  4. 优化查询语句

    • 避免 SELECT *,只查询必要字段。

8. 撤销已提交到Git远端的内容

步骤

  1. 本地回滚

    git revert <commit-hash>  # 创建新提交撤销旧提交
    
  2. 推送到远端

    git push origin <branch-name>
    
  3. 强制推送(慎用)

    • 若已强制推送过历史提交,需使用 --force
    git push origin <branch-name> --force
    

9. Maven依赖冲突解决

方法

  1. 显式声明依赖版本

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.example</groupId>
                <artifactId>my-component</artifactId>
                <version>1.0.0</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
  2. 使用 mvn dependency:tree

    • 查看依赖树,定位冲突的依赖路径。
  3. 排除冲突依赖

    <dependency>
        <groupId>com.example</groupId>
        <artifactId>my-lib</artifactId>
        <exclusions>
            <exclusion>
                <groupId>com.conflict</groupId>
                <artifactId>bad-lib</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    

10. Linux常用命令

awk 命令

  • 用途:文本处理,按行/列提取数据。
    # 提取日志中错误级别为ERROR的行
    awk '/ERROR/ {print $0}' /var/log/app.log
    

查看日志命令

  1. tail -f

    tail -f /var/log/app.log
    
  2. grep + tail

    tail -n 100 /var/log/app.log | grep "ERROR"
    
  3. less 分页查看

    less /var/log/app.log
    

11. Elasticsearch倒排索引原理

  • 核心思想
    • 将文档拆分为词项(term),建立词项到文档的映射。
    • 示例:
      文档1: "Java is great"
      文档2: "Python is awesome"
      倒排索引:
      Java -> [文档1]
      is -> [文档1, 文档2]
      great -> [文档1]
      Python -> [文档2]
      awesome -> [文档2]
      

12. 单点登录(SSO)

  • 实现方式
    1. OAuth2:通过第三方认证服务(如Keycloak)颁发令牌。
    2. JWT:用户登录后生成加密令牌,跨域传递。
    3. Session共享:使用Redis存储会话,共享Session ID。

13. 接口 vs 抽象类

特性接口抽象类
方法实现默认方法、静态方法可有实现可有抽象方法和具体方法
构造函数
继承多实现单继承
访问修饰符方法默认 public可定义 protected

14. Java泛型

理解

  • 作用:编译时类型检查,避免类型转换错误。
  • 类型擦除:运行时泛型信息被擦除(如 List<String> 变为 List)。

底层实现

  • 桥接方法:解决方法重写的类型擦除问题。
    class Box<T> {
        public T get() { ... }
    }
    // 编译后生成桥接方法
    public Object get() {
        return get();
    }
    

15. JVM垃圾回收机制

新生代 vs 老年代设计

  • 新生代
    • 标记-复制算法:减少碎片(如 Eden + Survivor 区)。
  • 老年代
    • 标记-整理算法:避免碎片化(如 CMS、G1)。

减少内存碎片的机制

  1. 标记-整理算法:移动存活对象到连续内存。
  2. 分区回收:G1 将堆划分为 Region,按需回收。

16. Full GC触发条件

  1. 老年代空间不足
  2. 元空间(Metaspace)不足
  3. System.gc() 调用
  4. 直接内存不足(如 ByteBuffer.allocateDirect())。

17. 排查Full GC原因

  1. 分析GC日志
    jstat -gcutil <pid> 1000 10
    
  2. 使用 jmap 生成堆转储
    jmap -dump:format=b,file=heap.bin <pid>
    
  3. 工具分析:MAT(Memory Analyzer)定位内存泄漏。

18. 不适合建索引的字段

  1. 低基数字段:如性别(男/女)。
  2. 频繁更新的字段:索引维护开销大。
  3. 查询条件不常用的字段:索引利用率低。

19. 初始化账户服务使用消息队列的原因

  1. 解耦:账户服务与下游系统(如短信、邮件)解耦。
  2. 异步处理:提高响应速度,避免同步等待。
  3. 削峰填谷:应对流量高峰,防止系统崩溃。

20. 跨域Cookie共享

  • 解决方案
    1. 共享域名:使用 .aliyun.com 作为公共域名。
    2. OAuth2 Token:通过认证服务颁发跨域令牌(如 JWT)。
    3. 代理服务器:通过 Nginx 合并域名请求。

21. MCP协议

  • 可能指:Microservices Communication Protocol(微服务通信协议),但需具体上下文确认。

22. FunctionCall vs MCP

  • FunctionCall:普通函数调用。
  • MCP:假设为特定通信协议,需明确其定义。

23. Integer转int注意事项

  • 自动拆箱Integernull 时会抛出 NullPointerException
    Integer a = null;
    int b = a; // 抛出 NullPointerException
    
  • 建议:使用 Objects.requireNonNull()if (a != null) 检查。

1. TransmittableThreadLocal 如何在子线程获取

  • 核心流程
    1. 捕获(Capture):父线程提交任务前,通过 Transmitter.capture() 捕获所有 TransmittableThreadLocal 的值。
    2. 任务包装:使用 TtlRunnable.get(task)TtlCallable.get(callable) 包装任务,确保子线程执行时能恢复数据。
    3. 重放(Replay):子线程执行任务时,通过 Transmitter.replay(captured) 将父线程的值复制到子线程的 TransmittableThreadLocal 中。
    4. 清理(Restore):任务完成后,通过 Transmitter.restore(backup) 恢复子线程的原始状态,避免污染。
  • 代码示例
    TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
    threadLocal.set("Parent Value");
    
    Runnable task = () -> {
        System.out.println("Child Thread Value: " + threadLocal.get());
    };
    
    // 包装任务以支持线程间传递
    Runnable ttlTask = TtlRunnable.get(task);
    new Thread(ttlTask).start();
    

2. Java中的锁分类

锁类型重入性互斥性公平性适用场景
synchronized独占非公平简单同步,低并发场景
ReentrantLock独占公平/非公平替代 synchronized,灵活控制
ReadWriteLock写独占/读共享公平/非公平读多写少的场景
StampedLock写独占/读共享非公平高性能读多写少,需谨慎使用
分布式锁❌(可扩展)独占非公平跨节点资源互斥(如 Redis)

3. Redisson vs setnx 实现分布式锁的优势

特性Redissonsetnx 命令
自动续期✅(通过看门狗机制自动延长锁过期时间)❌(需手动续期)
异常处理✅(自动释放锁,避免死锁)❌(需手动处理网络异常)
API 简化✅(提供高阶 API,如 RLock❌(需手动编写 Lua 脚本)
性能优化✅(减少网络延迟,支持批量操作)❌(单命令操作)
可重入性✅(支持同一线程多次加锁)❌(需自行管理)

4. Redis 热 Key 问题

  • 定义:访问频率极高的 Key,导致 Redis 单点压力集中。
  • 影响
    • CPU 负载过高。
    • 网络流量激增。
    • 数据库雪崩风险(缓存失效后请求穿透到 DB)。
  • 解决方法
    1. 缓存预热:提前加载热点 Key。
    2. 数据分片:将热点 Key 分散到多个 Redis 实例。
    3. 本地缓存:使用 Caffeine 等本地缓存兜底。
    4. 限流降级:对高频请求进行限流。
    5. 异步更新:通过队列异步更新缓存。

5. G1 vs ZGC

特性G1ZGC
目标可预测的 STW 时间(<1 秒)极低延迟(<10ms)
堆大小适合 10GB~几十 GB适合 TB 级别
内存布局划分为 Region(Eden/Survivor/Old/Humongous)划分为 Region(大小固定为 2MB/32MB)
回收算法标记-整理(减少碎片)染色指针(无需移动对象)
适用场景大内存应用,平衡延迟和吞吐量超低延迟场景(如金融交易系统)
配置复杂度较低较高

6. 用户超时后前端处理

  • 前端逻辑
    const tokenExpirationTime = 3600000; // 1小时
    let lastActionTime = Date.now();
    
    // 每分钟检查一次
    setInterval(() => {
        const currentTime = Date.now();
        if (currentTime - lastActionTime >= tokenExpirationTime) {
            alert("会话已超时,请重新登录!");
            window.location.href = "/login";
        }
    }, 60000);
    
    // 用户操作时更新时间
    document.addEventListener("mousemove", () => {
        lastActionTime = Date.now();
    });
    

7. 监控超时请求

  • 实现步骤
    1. 数据收集:记录每次请求的响应时间。
    2. 聚合分析:按时间窗口统计平均响应时间或超时率。
    3. 趋势检测:使用滑动窗口或指数加权移动平均(EWMA)检测异常。
    4. 阈值触发:当超时率连续超过阈值(如 800ms),触发告警或切换备用服务。
  • 示例工具:Prometheus + Grafana 实时监控。

8. Zipkin 链路追踪

  • 原理:通过 Sleuth 生成唯一 Trace ID 和 Span ID,记录请求的调用链。
  • 配置步骤
    1. 引入依赖
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-zipkin</artifactId>
      </dependency>
      
    2. 配置 Zipkin 地址
      spring:
        zipkin:
          base-url: http://zipkin-server:9411
          sampler:
            probability: 1.0
      
    3. 启动后访问http://localhost:9411 查看链路详情。

9. 日志框架选择与实现

框架同步 vs 异步底层实现适用场景
Log4j同步/异步使用 AsyncAppender 实现异步高性能要求
Logback同步/异步内置异步支持(AsyncAppender简单配置,灵活扩展
SLF4J依赖具体实现门面模式,不直接处理日志写入解耦业务代码与日志框架
  • 异步日志优势
    • 减少主线程阻塞。
    • 提升吞吐量(适用于高并发场景)。
  • 注意事项
    • 需确保日志顺序(使用队列保证顺序性)。
    • 避免日志丢失(使用可靠队列,如 Kafka)。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~Yogi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值