当前位置: 首页 > news >正文

面试题之高频面试题

最近开始面试了,410面试了一家公司 针对自己薄弱的面试题库,深入了解下,也应付下面试。在这里先祝愿大家在现有公司好好沉淀,定位好自己的目标,在自己的领域上发光发热,在自己想要的领域上(技术管理、项目管理、业务管理等)越走越远!希望各位面试都能稳过,待遇都是杠杠的!因为内心还是很慌,所以先整理所有高频的面试题出来,包含一些业务场景及java相关的所有面试。

JAVA基础


1.java的各种锁

乐观锁、悲观锁、公平锁,非公平锁、排他锁、共享锁、重入锁、偏向锁、重量级锁

  • 基础锁概念:
    • 乐观锁:
      • 假设并发冲突很少发生
      • 实现方式:版本号机制、CAS操作
      • 典型实现:Atomic类、StampedLock的乐观读
      • 使用场景:读多写少,冲突概率低
    • 悲观锁:
      • 假设并发冲突经常发生
      • 实现方式:synchronized、ReentrantLock
      • 典型特点:先加锁再访问
      • 使用场景:写多读少,重读概率高
    • 公平锁:
      • 按请求顺序分配锁
      • 实现:ReentrantLock(true)
      • 优点: 避免线程饥饿
      • 缺点:吞吐量较低
    • 非公平锁:
      • 允许线程“插队”获取锁
      • 实现:synchronized、ReentrantLock(false)
      • 优点:吞吐量高
      • 缺点:可能造成线程饥饿
    • 排他锁(独占锁)
      • 同一个时刻只允许有一个线程持有
      • 实现:synchronized、ReentrantLock、ReentrantReadWriteLock.WriteLock
    • 共享锁:
      • 允许多个线程共同持有
      • 实现:ReentrantReadWriteLock.ReadLock、Semaphore
  • 高级锁特征
    • 重入锁(Reentrant Lock)
      • 线程可以重复获取已持有的锁
      • 实现:synchronized、ReentrantLock
      • 锁技术:每次重入计数+1,完全释放需解锁相同次数
      • 锁膨胀过程(JVM优化)
      • 无锁状态:初始状态
    • 偏向锁:优化单线程重复访问
    • 轻量级锁:多线程轻度竞争时自旋等待
    • 重量级锁:竞争激烈时转为OS互斥量
    • 自旋锁(Spin Lock)
      • 线程不立即阻塞,而是循环尝试获取锁
      • 减少线程上下文切换开销
      • 使用场景:锁持有时间短的场景
  • 特殊用途锁:
    • 分段锁(segment Lock)
      • 将数据分段,每段独立加锁
      • 典型实现:ConcurrentHashMap(JDK7版本)
      • 提高并发度,减少锁竞争
    • 邮筒锁(mailbox Lock)
      • 用于线程间通信的同步机制
      • 类似生产者-消费者模式中的交换区
    • 条件锁(condition Lock)
      • 基于条件的等待/通知机制
      • 实现:condition 接口
      • 比Object.wait()/notify更灵活
    • 分布式锁:
      • 跨JVM进程的锁机制
      • 常见实现:redis、zookeeper
      • 典型方案:RedLock算法
  • JUV包中的并发工具
    • 信号量(semaphore)
      • 控制同时访问特定资源的线程数量
      • 可做流量控制
    • 倒计时门闩(countDownLath)
      • 等待多个线程完成后再继续
    • 循环栅栏(cyclicBarrier)
      • 让一组线程互相等待到达屏障点
    • 相位器(Phaser)
      • 更灵活的多阶段同步屏障
  • 其他重要概念
    • 锁消除(Lock Elimination)
      • JVM在即时编译时消除不必要的锁
      • 基于逃逸分析判断对象不会逃逸出当前线程
    • 锁粗化(Lock coarsening)
      • 将多个锁的锁操作合并成一个更大范围的锁
      • 减少频繁加锁解锁的开销
    • 死锁(Dead Lock)
      • 多线程互相等待对方释放锁
      • 四个必要条件:互斥条件、请求与保持、不可剥夺、循环等待
    • 活锁(live lock)
      • 线程不断改变但无法继续执行
      • 类似“谦让过度”的情况
    • 锁饥饿(Lock starvation)
      • 某些线程长期无法获取资源
      • 常见于不合理的锁分配策略
  • 锁的选择建议:
    • 优先考虑内置锁:简单的synchronized
    • 需要灵活时:选择ReentrantLock或者ReentrantReadWriteLock
    • 读多写少场景:考虑stampedLock的乐观读
    • 高度并发计数:使用Atomic类
    • 资源池控制:使用semaphore
    • 线程协调:根据场景选择CountDownLatch/CyclicBarrier/Phaser

Q:分布式锁的实现逻辑?

A:分布式锁是解决分布式系统中资源互斥访问的关键技术.

  • 分布式锁核心特性要求:
    • 互斥性:同一时刻只有一个客户端能持有锁
    • 防死锁:持有锁的客户端崩溃后锁能自动释放
    • 容错性:部分节点宕机不影响锁服务的可用性
    • 可重入:同一客户端多次获取同一把锁
    • 高性能:获取/释放的操作要高效
  • 主流实现方案:
    • 基于数据库实现:
      • 实现流程:
        • 创建数据库表专门用来做分布式锁,每次分配一个锁就在数据库插入一条数据,释放则删除
      • 特点:
        • 简单易实现,性能较差(增加了IO开销)
        • 需要处理死锁和超时问题
        • 可考虑乐观锁版本号机制优化
    • 基于redis实现
      • 实现流程:
        • setnx方案:通过redis的setnx获取锁,当返回成功时处理业务逻辑,其底层通过LUA脚本保证原子性
        • RedLock算法(Redis官方推荐):尝试获取所有节点的锁,并获取耗时检查
      • 特点:
        • 性能优异
        • 需要处理锁续期问题(看门狗机制-只要客户端还活着即jvm进程未崩溃,就主动续约)
        • RedLock需要至少5个Redis主节点
        • 网络分区可能出现脑裂问题
    • 基于zookeeper实现
      • 实现流程:
        • 创建临时有序节点
        • 获取所有子节点
        • 判断是否最小节点,是则获取锁成功,否则监听前一个节点阻塞等待
      • 特点:
        • 可靠性高(cp系统)
        • 性能低于Redis方案
        • 天然解决锁释放问题(临时节点)
        • 实现相对复杂
    • 基于Etcd的实现
      • 实现流程:
        • 获取锁(租约机制)
        • 保持心跳持续锁
        • 释放锁
      • 特点:
        • 基于Raft协议强一致
        • 支持租约自动过期
        • 比zk更易实现
    • 关键性问题解决方案:
      • 锁续期问题:
        • redis方案:启动后台线程定期延长锁过期时间(Redisson的watchdog机制)
        • zookeeper方案:会话心跳自动维持
      • 锁误释放问题
        • 每个锁绑定唯一客户端标识(UUID)
        • 释放时校验标识(Lua脚本保证原子性)
      • 锁等待问题:
        • 实现公平锁(zk顺序节点)
        • 设置合理的等待超时时间
      • 集群故障处理
        • redis:主从切换可能导致锁丢失(RedLock可缓解)
        • zk:半数以上节点存活即可工作

各方案对比

方案一致性性能实现复杂度适用场景
数据库简单低频简单场景
Redis中等高频、允许偶尔失效
RedLock较强复杂高可靠性要求
Zookeeper复杂CP系统、分布式协调
Etcd中高中等Kubernetes环境、CP系统
  • 最佳实现:
    • 始终设置合理的锁超时时间
    • 实现锁的可重入人逻辑(如计数机制)
    • 添加锁获取失败的重试策略(带退避算法)
    • 关键操作记录审计日志
    • 生产环境建议使用成熟框架
      • java:redisson、curator
      • go:etcd/clientv3
      • python:python-redis-lock

Q:redlock如何保障redis主从切换时锁丢失问题?

问题描述:普通的redis主从架构中:客户端向主节点申请锁,主节点异步复制锁的信息到从节点中。若主节点崩溃,从节点升级为主节点时,可能尚未接受到锁信息,导致新主节点上没有锁信息,其他客户端可以获取相同的锁,破坏了互斥性

A:RedLock通过多节点独立获取+多数表决机制解决该问题。

  • 部署多个完全独立的redis主节点(无主从关系,建议至少部署5个)
  • 客户端依次向所有节点申请锁
  • 当获取多数节点(N/2+1)的锁时,才算获取成功
  • 锁的有效时间包含获取锁的时间消耗

Q:Redisson看门狗机制如何实现业务未处理完成,锁会自动续约?

A:Redisson的看门狗机制是其分布式锁实现的核心特性之一,它解决了业务处理时间超过锁超时时间的问题。

  • 核心原理:
    • 自动续期机制:当客户端获取锁后,会启动一个后台线程定期检查并延长锁的持有时间
    • 健康检查:只要客户端还“活着”(JVM进程未崩溃),锁就不会因为超时被意外释放
    • 默认配置:
    • 锁默认超时时间:30秒
    • 续期检查间隔:超时时间的1/3(默认10秒一次)

Q:什么是死锁,如何避免?

A:死锁(Deadlock)是指两个或多个进程/线程在执行过程中,由于竞争资源彼此通信而造成的一种互相等待的现象,若无外力干涉,这些进程/线程都将无法继续执行下去

  • 死锁的必要提交:
    • 互斥条件:资源一次只能由一个进程占用
    • 请求与保持条件:进程持有至少一个资源,并等待获取其他被占用资源
    • 不可剥夺条件:已分配给进程的资源,不能被其他进程强行夺取
    • 循环等待条件:存在一个进程等待的循环链
  • 死锁常见场景:
    • 数据库死锁
    • java多线程死锁
  • 预防策略(破坏必要条件)
必要条件破坏方法
互斥条件使用共享资源(如读写锁)
请求与保持条件一次性申请所有资源(All-or-Nothing)
不可剥夺条件允许抢占资源(设置超时/中断)
循环等待条件资源有序分配法(对所有资源排序,按固定顺序申请)
  • 避免策略(运行时判断)
    • 银行家算法:系统在分配资源先计算分配的安全性
    • 资源分配图算法:检查图中是否存在环
  • 检查与恢复
    • 检查机制:
      • 定期检查资源分配图
      • 使用等待图(wait-for graph)检测环
    • 恢复措施:
      • 进程终止:强制终止部分死锁进程
      • 资源抢占:回滚并抢占部分资源
  • 解决:
    • java中,锁的顺序化,try lock实现
    • 数据库中,优先事务及重试机制
2.JVM垃圾回收原理及如何优化
  • 垃圾回收核心概念:
    • 可达性分析算法:通过GC Roots对象作为起点,向下搜索引用链,不可达的对象即为垃圾
    • GC Roots包括:
      • 虚拟机栈中引用的对象
      • 方法区中类静态属性引用的对象
      • 方法区中常用引用的对象
      • 本地方法栈中JNI引用的对象
  • 内存分代模型
    • JVM将堆内存划分为不同的代际
堆内存结构
┌───────────────────────┐
│       Young Gen       │
│ ┌─────┐ ┌─────┐ ┌─────┐│
│ │Eden │ │S0   │ │S1   ││
│ └─────┘ └─────┘ └─────┘│
├───────────────────────┤
│       Old Gen         │
└───────────────────────┘
│   Permanent Gen/Metaspace  │
└───────────────────────┘
  • 主流的垃圾回收器:
    • 新生代回收器:
      • serial:单线程,复制算法
      • parNew:serial的多线程版本
      • parallel scavenge:吞吐量有限
    • 老年代回收器:
      • serial old:单线程,标记-整理算法
      • parallel old:parallel scavenge的老年代斑斑
      • CMS:低延迟,标记-清楚算法
    • G1回收器:
      • 面向服务端应用
      • 将堆划分为多个Region
      • 可预测的停顿时间模型
    • ZGC/Shenandoah
      • JDK11+引入的超低延迟回收器
      • 停顿时间不超过10ms
      • 支持TB级堆内存
  • 垃圾回收优化策略:
    • 关键JVM参数:
      • 设置初始和最大堆大小,老年代和新生代比例,eden/survivor比例,启用G1回收器,启用CMS回收器
    • 优化原则:
      • 内存分配优化:
        • 避免过大的对象直接进入老年代
        • 合理设置新生代大小,减少过早晋升
        • 监控对象年龄分布,调整晋升阈值
      • GC策略选择:
        • 吞吐量优先:parallel scavenge+parallel old
        • 低延迟优先:CMS/G1/ZGC
        • 大内存应用:G1/ZGC/Shenandoah
  • 常见问题解决方案:
    • 频繁full gc
      • 检查内存泄露
      • 调整老年代大小
      • 优先对象分配模式
    • 长时间GC停顿
      • 考虑切换到G1或者ZGC
      • 减少存活对象数量
    • 增加堆内存
      • 内存碎片问题:
        • 使用标记-整理算法回收器
        • 适当减少-xxcmsInitiatingOccupancyFraction值
    • 定期重启服务
      • 使用优化技巧
        • 对象池化:复用对象减少GC压力
        • 本地缓冲控制:合理使用weak /soft reference
        • 集合优化:预估大小避免扩容
        • 流处理:及时关闭资源
      • 监控工具:
        • jstat -gcutil pid
        • visualVM
        • GCViewer分析GC日志
        • Arthas实时诊断
  • 不同场景下优化建议
    • web应用
      • 推荐G1回收器,关注会话对象生命周期,优化缓冲策略
    • 大数据处理
      • 增加新生代比例,考虑使用parallel回收器,监控大对象分配
    • 金融交易系统:
      • 优先考虑ZGC/shenandoah,严格控制停顿时间,减少不可预测的对象分配

Spring相关面试题


1.Spring AOP底层实现原理是什么?
  • Spring AOP(面向切面编程)的底层实现基于动态代理技术,主要通过两种实现方式:JDK动态代理和CGLIB字节码生成。当目标类实现了接口,Spring默认使用了JDK动态代理,否则使用CGLIB方式,而spring-boot选择了VCGLIB方式来实现。
    • JDK动态代理(基于接口)
      • 适用条件:目标类实现了至少一个接口
      • 代理对象通过JDK直接生成,实现目标类的接口,并通过反射调用目标类
      • 特点:
        • 运行时生成接口的代理类
        • 通过InvocationHandler拦截方法调用
        • 性能较好,但只能代理接口方法
    • CGLIB字节码生成(基于子类)
      • 适用条件:不能使用final
      • 适用对象:目标类没有实现接口
      • CGLIB通过CFLIB-ASM操作框架生成,继承目标类,通过子类调用父类的方式进行调用目标方法
      • 特点:
        • 通过ASM库直接生成目标类的子类(Enhancer)
        • 可以代理普通类方法(包括非public方法)
        • 创建代理对象速度较慢,但执行效率高
    • 代理创建流程
      • Spring AOP创建代理的核心流程:
        • 解析切面配置
        • 通过@Aspect注解或者XML配置识别切面
        • 解析切入点表达式(Pointcut)
        • 创建代理工厂(proxyFactory)
        • 选择代理机制
        • 生成代理对象
      • JDK代理:proxy.newProxyInstance()
      • CGLIB:enhance.create()
    • 拦截器链执行
      • Spring AOP通过责任链模式执行增强逻辑
      • 增强类型与顺序执行
    • spring aop支持了五种通知类型
      • @Aroud环绕通知
      • @before前置通知
      • 目标方法执行
      • @AfterReturing(返回通知,正常返回时执行)
      • @After(后置通知,finally块中执行)
      • @AfterThrowing(异常通知,抛出异常时执行)
    • 性能优化与实现
      • Spring对AOP进行了多项优化
        • 缓冲机制
          • 代理类缓冲DefaultAopProxyFactory
          • 拦截器链缓冲AdvisedSupport
        • 预过滤
          • 预先排除不可能匹配的方法
          • 选择性代理
          • 支队匹配切入点的方法生成代理逻辑
          • 其他方法直接调用目标方法
  • 与AspectJ的关系
特性Spring AOPAspectJ
实现方式运行时动态代理编译时/加载时织入
性能较好最优(编译期完成)
功能范围仅方法级别字段、构造器、静态块等
依赖仅Spring核心需要AspectJ编译器/织入器
适用场景简单切面需求复杂切面需求
  • 总结:
    • Spring AOP的底层实现本质上是基于代理模式的运行时增强,其核心特点是
      • 非侵入性:通过代理实现,不修改原始代码
      • 灵活性:支持多种通知类型和切入点表达式
      • 可扩展性:可与AspectJ部分功能集成
      • 性能平衡:通过缓存和优化策略保证运行时效率

组合面试题


1.如何有效的阻止订单重复提交和支付

理论上只会在用户在下单的这个动作可能因为网络抖动、RPC重试等进行多次下单的操作,其他步骤确认订单只是修改订单状态,跳转支付和确认支付这些不会出现多次支付的问题。所以该题主要是针对用户多次调用下单接口怎么处理即下单接口的幂等性问题。

  • 订单重复提交问题
    • 前端防重复提交方案
      • 按钮置灰等操作
      • PRG模式:post/redirect/get模式,用户点击表单时重定向跳转到其他页面。
      • token机制,在用户进入订单界面前生成固定的token,前端限制一个token调用时,后端拦截token的一次性,做请求拦截限制
      • 请求拦截:通过axios拦截器拦截信息
    • 后端接口设计
      • 幂等性设计
        • 每次请求先配合客户端生成一个唯一id,可由订单id+用户id+确认标识做绑定,同一接口,每次调用的id一致则不生成新的订单,注意标识符的失效时间
        • 请求参数中带有时间戳与当前时间对比,若时间太长则默认为重复请求
        • 请求状态检查,根据日志查询、用户订单关联查询是否有重复数据
      • 数据库唯一约束
        • 先获取数据库唯一ID来处理
      • redis原子操作
        • setnx操作,对同一个订单id+用户id+确认标识做绑定,设置失效时间,进行处理
  • 支付方重复方案
    • 订单状态机制
    • 第三方支付幂等
    • 支付结果异步核对
  • 分布式系统解决方案
    • 分布式锁
    • 消息队列去重
  • 异常处理机制
    • 补偿事务处理
    • 人工审核接口
  • 监控与报警
    • 重复请求监控 设置ip白名单防止恶意操作
  • 建议:
    • 多层次防御:前端+后端+数据库约束等
    • 核心原则:所有写操作必须实现幂等性接口
    • 关键数据:订单号、用户ID、时间戳组合防重复
    • 状态管理:严格的状态机控制流程
    • 补偿机制:自动核对+人工干预双重保险
  • 技术选择
场景推荐方案优点
简单单体系统本地锁+数据库唯一约束实现简单
分布式高并发Redis分布式锁+消息队列扩展性好
金融级支付系统状态机+定时核对+人工干预可靠性最高
旧系统改造前端Token+后端幂等接口侵入性最小

2.RestTemplate 如何优化连接池
  • restTemplate默认是没有连接池,他的调用原理是每次都会创建一个HTTP连接,默认使用simpleClientHttpRequestFactory去创建连接,底层通过HttpURLConnection创建连接。在高并发的条件下,会无上限的创建连接,消耗系统资源。所以需要通过连接池来限制最大连接数,当请求的域不是很多且不随机的情况下,还可以复用同一个域的HTTP连接;
  • HTTP请求流程:在我们发起一个http请求连接的时候,会对域名解析,连接之前的三次握手,如果是HTTPS还需要传递安全证书,以及请求完成之后的4次挥手。但是我们真正请求和响应只在其中的一小环节,所以我们通过一个连接池就可以对同一个域来建立一个长链接,就无需执行每次的无关业务请求的动作,这样就实现了连接的复用。
  • 通过resttemplate来配置连接池的话有HttpClient和OkHttp.
  • 具体实现:
    • 代码上引入httpclient连接池的包,修改RestTemplate请求工厂,将默认工厂的simpleClientHttpRequestFactory换成HttpClientFactory,在为这个工厂配置请求bean。最后去设置连接池参数信息。设置最大连接数,根据QPS的响应时间平均值来设置,设置某个域的长链接复用,但是如果该值太小,比如设置2,那么只会创建两个长链接,这样后续的接口会进行阻塞等待。
    • 在实现连接池的方法时,需要注意以下几点:
      • 路由区分:对重要API设置独立的路由连接数
      • 异常处理:配置重试机制
      • DNS刷新:避免DNS缓冲问题
      • 连接存活时间
参数建议值说明
setMaxTotal100-500最大连接数,根据服务器配置调整
setDefaultMaxPerRoute50-100每个路由(host:port)的最大连接数
setConnectTimeout3000-5000ms建立TCP连接的超时时间
setSocketTimeout5000-10000ms数据传输超时时间
setConnectionRequestTimeout1000-2000ms从连接池获取连接的超时时间
evictIdleConnections30-60s空闲连接回收时间

工具类应用


1.git如何撤回已提交的push代码

使用idea集成可视化界面操作

  • 已提交 未推送
    • 使用idea,找到提交的版本,选择undo commit
  • 已提交 已推送
    • revert commit->本地代码回滚到提交前的版本->在push一下 将远程仓库代码覆盖上
  • 已回滚 代码恢复
    • 选择刚才已经回滚的代码->cherry pick还原已经写好的代码


http://www.mrgr.cn/news/98811.html

相关文章:

  • 探索关系型数据库 MySQL
  • 优化方法介绍(二)——BFGS 方法介绍
  • 信息科技伦理与道德0:课程安排
  • rk算力集群usb做网卡
  • jenkins凭据管理(配置github密钥)
  • 十三种物联网/通信模块综合对比——《数据手册--物联网/通信模块》
  • openGauss使用指南与SQL转换注意事项
  • 虚幻基础:ue引擎的碰撞
  • Freertos----信号量
  • 【DVWA 靶场通关】 File Inclusion(文件包含漏洞)
  • Orin NX开发板的烧录脚本注解
  • C++11智能指针深度解析:在Visual Studio中高效管理内存
  • Linux网络编程实战:从字节序到UDP协议栈的深度解析与开发指南
  • 【ROS】DWA 规划器
  • Day2-UFS协议栈
  • 基础智能体的进展与挑战——从类脑智能到进化、协作和安全系统(译文)
  • 【c语言】深入理解指针2
  • 1.凸包、极点、极边基础概念
  • Linux 常用命令总结
  • 手动安装 VMware Tools 并设置虚拟机共享 Windows 文件夹