2026-10-23 12:11:37
OPPO后端开发秋招一面整体以基础知识考察为主,涵盖多线程、数据库、分布式、Java集合与框架等内容,项目与实习经历询问较少,以下为详细解析:
考察内容与解析需简洁清晰阐述个人教育背景、技术栈、项目经历(简要提及核心项目与成果)、技术热情方向等,让面试官快速了解你的基本情况与技术优势。
是Java中可重入互斥锁,基于AQS(AbstractQueuedSynchronizer)实现。
可重入性:同一线程可多次获取同一把锁,每获取一次锁计数器加1,释放一次减1,计数器为0时锁真正释放。
公平锁与非公平锁:公平锁按请求锁顺序获取,非公平锁可能插队获取,非公平锁吞吐量更高。
与synchronized区别:ReentrantLock功能更灵活,如可响应中断、可设置超时、可公平锁等;synchronized是Java关键字,使用简单,由JVM实现,在JDK1.6后性能大幅提升。
线程池类型:
newFixedThreadPool:固定大小线程池,核心线程数与最大线程数相同,可控制并发数,适用于任务量较稳定场景。
newCachedThreadPool:可缓存线程池,核心线程数为0,最大线程数为Integer.MAX_VALUE,线程空闲60秒后回收,适用于任务多且执行时间短场景。
newSingleThreadExecutor:单线程线程池,只有一个核心线程,保证任务按顺序执行,适用于需要顺序执行任务场景。
newScheduledThreadPool:定时任务线程池,支持定时与周期性任务执行。
核心参数:
corePoolSize:核心线程数,线程池保持的最小线程数。
maximumPoolSize:最大线程数,线程池允许创建的最大线程数。
keepAliveTime:线程空闲时间,超过该时间未任务则回收线程(核心线程除外)。
unit:时间单位,与keepAliveTime配合使用。
workQueue:任务队列,用于存放等待执行的任务。
threadFactory:线程工厂,用于创建新线程。
handler:拒绝策略,当任务队列满且线程数达到最大线程数时采取的策略,如AbortPolicy(抛异常)、CallerRunsPolicy(调用者执行)等。
数据结构:主要采用B+树。
选择原因:
磁盘IO友好:B+树是平衡多路搜索树,树高度较低,可减少磁盘IO次数,提高查询效率。
范围查询高效:B+树叶子节点通过指针连接,范围查询时只需定位到起始节点,然后顺序遍历叶子节点即可,无需回溯上层节点。
查询稳定性高:B+树所有查询都需从根节点到叶子节点,查询路径长度相同,查询效率稳定。
选择合适列:选择经常用于查询条件、排序、分组的列,如WHERE子句、ORDER BY子句、GROUP BY子句中的列。
避免过多索引:索引会占用存储空间,且插入、更新、删除操作需维护索引,过多索引会影响写入性能。
考虑列选择性:列选择性越高,索引效果越好,如性别列选择性低,不适合建索引。
复合索引顺序:遵循最左前缀原则,将选择性高、区分度大的列放在前面。
避免索引失效:如使用函数、运算符、模糊查询(以%开头)等可能导致索引失效。
选择一个核心项目,介绍项目背景、目标、技术栈、架构设计等。
重点阐述项目中遇到的最大问题,如性能瓶颈、数据一致性问题、并发问题等,并说明解决方案,体现问题分析与解决能力。
可提及阅读经典技术书籍(如《Java编程思想》《深入理解Java虚拟机》等)、在线课程(如慕课网、极客时间等)、技术博客(如CSDN、掘金等)、开源项目学习、参与技术社区讨论等。
读未提交(Read Uncommitted):一个事务可读取另一个事务未提交的数据,存在脏读、不可重复读、幻读问题。
读已提交(Read Committed):一个事务只能读取另一个事务已提交的数据,解决了脏读问题,但存在不可重复读、幻读问题。
可重复读(Repeatable Read):同一事务内多次读取同一数据结果一致,解决了脏读、不可重复读问题,但存在幻读问题(InnoDB引擎通过MVCC与间隙锁解决幻读问题)。
串行化(Serializable):所有事务串行执行,避免了脏读、不可重复读、幻读问题,但并发性能最低。
多版本并发控制:通过保存数据在某个时间点的快照来实现并发控制,读操作不加锁,提高并发性能。
实现原理:
隐藏字段:每行记录有创建版本号(创建事务ID)、删除版本号(删除事务ID)。
ReadView:事务在某一时刻生成的读视图,包含事务ID、活跃事务列表等信息,用于判断记录对当前事务是否可见。
可见性判断:根据记录的创建版本号、删除版本号与ReadView中的事务ID、活跃事务列表比较,判断记录是否可见。
选举触发:当主节点挂了或网络分区导致其他节点无法与主节点通信时,触发选举。
候选人发起选举:节点等待随机超时时间后,若未收到主节点心跳,则自增任期号,转为候选人,发起投票请求。
投票规则:每个节点在一个任期内只能投一票,且优先投给收到请求中任期号最大且日志更完整的候选人。
选举成功:候选人获得多数节点投票后成为新主节点,开始发送心跳通知其他节点。
String:最基本数据类型,可存储字符串、整数、浮点数,支持增删改查等操作。
Hash:键值对集合,适合存储对象,如用户信息。
List:链表结构,支持两端插入与弹出操作,可用于消息队列、最新消息排行等。
Set:无序集合,元素唯一,支持交并差等集合操作。
Zset(Sorted Set):有序集合,元素唯一,每个元素关联一个分数,根据分数排序,可用于排行榜等场景。
方案一:先更新数据库,再删除缓存:可能存在更新数据库成功但删除缓存失败情况,导致数据不一致。可通过重试机制、消息队列保证最终一致性。
方案二:先删除缓存,再更新数据库:可能存在删除缓存后更新数据库前有读请求将旧数据写入缓存,导致数据不一致。可通过延迟双删(删除缓存、更新数据库、延迟一段时间再删除缓存)解决。
缓存穿透:查询不存在的数据,导致请求直接访问数据库。可通过布隆过滤器、缓存空对象解决。
缓存击穿:热点数据缓存过期时,大量请求同时访问数据库。可通过设置热点数据永不过期、加互斥锁解决。
缓存雪崩:大量缓存同时过期或缓存服务器宕机,导致大量请求直接访问数据库。可通过均匀设置缓存过期时间、多级缓存、限流降级等解决。
JDK1.7:采用分段锁技术,将数据分成多个段,每个段是一个独立的哈希表,段与段之间互不影响,提高并发性能。
JDK1.8:取消分段锁,采用CAS+synchronized实现,通过节点数组+链表+红黑树结构存储数据,当链表长度超过阈值时转为红黑树,提高查询效率。
实例化:通过反射创建Bean实例。
属性赋值:为Bean实例设置属性值。
BeanPostProcessor前置处理:调用BeanPostProcessor的postProcessBeforeInitialization方法。
初始化:调用Bean的初始化方法(如实现InitializingBean接口的afterPropertiesSet方法、自定义init-method方法)。
BeanPostProcessor后置处理:调用BeanPostProcessor的postProcessAfterInitialization方法。
使用:Bean实例可供应用程序使用。
销毁:调用Bean的销毁方法(如实现DisposableBean接口的destroy方法、自定义destroy-method方法)。
REQUIRED:默认传播行为,若当前存在事务,则加入该事务;若不存在,则创建新事务。
SUPPORTS:若当前存在事务,则加入该事务;若不存在,则以非事务方式执行。
MANDATORY:必须在事务中执行,若当前不存在事务,则抛出异常。
REQUIRES_NEW:创建新事务,若当前存在事务,则挂起当前事务。
NOT_SUPPORTED:以非事务方式执行,若当前存在事务,则挂起当前事务。
NEVER:不能在事务中执行,若当前存在事务,则抛出异常。
NESTED:若当前存在事务,则在嵌套事务中执行;若不存在,则创建新事务。