字节、美团、快手核心部门面试总结(真题解析)

共 6111字,需浏览 13分钟

 ·

2020-11-14 21:42


前言


由于个人发展的原因,前段时间又出去面试了,这次面试目标比较清晰,主要面一些业务量比较大、业务比较核心的部门。


前前后后面了一个多月,面了不少公司,面试轮次二三十轮应该是有的。


按照自己的习惯,将这次面试过程中的一些经验总结、心得体会记录下来,自己留个记录,也希望可以帮助到一些同学。


个人情况


几个关键词:Java后端、5年经验、普通本科、面试时在阿里。



面试情况


B站、爱奇艺:投了简历没下文,扎心了

字节:3轮技术 + hr,offer

快手:4轮技术 + hr,offer

美团:3~5轮技术 + hr ,3个部门offer(可并行面多部门)

途虎:3轮技术 + hr + CTO,offer

得物:1面通过,2面需要去现场(上海),放弃

贝壳:1面通过,2面面试官表示业务并发量不大,放弃

猿辅导:hr表示只能在19点之前面试,19点后已经下班(过分了),放弃



真题:Java 基础


常见八股文


Java 常见的八股文题目这次感觉问的不是很多(其实是我忘了问了哪些题),而且考来考去就那些题,可以参考:《4 年 Java 经验面试总结、心得体会》里面的题目即可。


比较常问的几个知识点之前已经专门出过面试解析:《面试阿里,HashMap 这一篇就够了》、《面试必问的线程池,你懂了吗?》、《BATJTMD 面试必问的 MySQL,你懂了吗?


近期也在开始准备剩余部分的面试题解析,redis 和 spring+mybatis 已经在路上,有兴趣的可以关注一波,不过首先还是优先保证质量,所以可能效率不会那么高。


这边列举几个我印象比较深的题目。



ConcurrentHashMap的并发扩容


ConcurrentHashMap 在扩容时会计算出一个步长(stride),最小值是16,然后给当前扩容线程分配“一个步长”的节点数,例如16个,让该线程去16个节点进行扩容操作(节点从老表移动到新表)。


如果在扩容结束前又来一个线程,则也会给该线程分配一个步长的节点数让该线程去扩容。依次类推,以达到多线程并发扩容的效果。



AQS中有多个线程并发添加到队列中,需要做并发控制吗


这个是需要的,源码中是通过 CAS 来进行并发控制。


在 addWaiter(Node mode) 方法中,如果并发添加节点,则只会有一个线程成功,另一个会失败,从而走到 enq(node) 方法中去进行重试。


说实话,虽然看过源码,但是真的很难记得这么多细节,因为 Java 面试要准备的东西实在太多了,所以这边可以利用一个小技巧。


小技巧:首先并发控制肯定是要做的,这个不难推测;接着,如果注意观察的话,不难发现在 JDK 源码中做并发控制的方式大多是 CAS,所以当你不知道怎么做并发控制的时候,可以直接蒙个 CAS,很有可能就被你蒙对了。




线程池有多个线程同时没取到任务,会全部回收吗


这个题不是很好理解,举个例子:线程池核心线程数是5,当前工作线程数为6(6>5,意味着当前可以触发线程回收),如果此时有3个线程同时超时没有获取到任务,这3个线程会都被回收销毁吗。


也是非常刁钻的一题,非常细节。但是即使我们记不得源码的细节了,还是有办法去推敲出来的。


思路:这道题的核心点在于有多个线程同时超时获取不到任务。正常情况下,此时会触发线程回收的流程。


但是我们知道,正常不设置 allowCoreThreadTimeOut 变量时,线程池即使没有任务处理,也会保持核心线程数的线程。


如果这边3个线程被全部回收,那此时线程数就变成了3个,不符合核心线程数5个,所以这边我们可以首先得出答案:不会被全部回收。这个时候面试官肯定会问为什么?


根据答案不难推测,为了防止本题的这种并发回收问题的出现,线程回收的流程必然会有并发控制。


what?又是并发控制,那不是又可以用到上面刚讲的“小技巧”?没错,源码中确实又是使用 CAS 来进行并发控制,从而保证在本例中只会有一个线程成功被回收。


本例相关源码在:getTask() 方法中。



多个AOP的顺序怎么定


通过 Ordered 和 PriorityOrdered 接口进行排序。


思路:这个问题其实我也也没准备过,但是我知道在 Spring 中有专门定义了 Ordered 和 PriorityOrdered 接口来实现排序功能,例如:对 BeanFactoryPostProcessor 的排序就是使用的这两个接口,所以这边我是直接猜测的用的这两个接口,结果还真是,所以平时多积累还是挺重要的



Object的wait()方法为什么需要先获取锁


这个问题说难也难,说简单也简单。


说简单是因为,大家应该都记得有道题目:“sleep和wait的区别”,答案中有一项就是:“wait会释放对象锁,sleep不会”,既然要释放锁,那必然要先获取锁。


说难是因为如果没有联想到这个题目并且没有了解 wait() 的底层原理,可能就完全没头绪了。


在 JVM 源码中,wait() 的底层逻辑会释放当前占有的锁,在释放前会进行锁的检查,如果当前线程没有持有锁,则会直接抛出 IllegalMonitorStateException。



synchronize底层维护了几个列表存放被阻塞的线程


这题是紧接着上一题的,很明显面试官想看看我是不是真的对 synchronize 底层原理有所了解。


synchronize 底层使用了3个双向链表来存放被阻塞的线程,3个双向链表分别是:_cxq(Contention queue)、_EntryList(EntryList)、_WaitSet(WaitSet)


当线程获取锁失败进入阻塞后,首先会被加入到 _cxq 链表,_cxq 链表的节点会被进一步转移到 _EntryList 链表。


当持有锁的线程释放锁后,_EntryList 链表头结点的线程会被唤醒,该线程称为 OnDeck 线程,然后该线程会尝试抢占锁。


而当我们调用 wait() 时,线程会被放入 _WaitSet,直到调用了 notify()/notifyAll() 后,线程才被重新放入 _cxq 或 _EntryList,默认放入 _cxq 链表头部。



真题:算法题、手写代码


单链表反转、合并两个有序链表、版本号比较


送分题,出这种题说明你前面面的不错,笔试题只是走个过场,力扣原题:21、165、206。


多线程按顺序输出ABC若干次


思路:考察多线程基础,Semaphore、Lock + Condition、synchronize 都有解法。



单例模式


手写单例模式,加上以前几次的面试,我有印象的至少有两个大厂问过了,还是挺重要的。通常有五种:懒汉、饿汉、双重检测、静态内部类、枚举,参考之前的文章:《单例模式详解



手写阻塞队列


参考 ArrayBlockingQueue,实现 put 和 take 方法即可。



手写HashMap


JDK 1.8 的版本有点太复杂了,可以写 JDK 1.7 的版本,使用链表 + 数组实现,实现 get 和 put 方法即可。



输出一个二叉树的宽度


思路二叉树的层序遍历(力扣102)的简单变形,记录下每层的节点个数,取最大值即可。



阿拉伯数字转汉字


例如:输入123.123,输出一百二十三点一二三。


思路:将“零一二三四五六七八九”、“十百千万亿”这些结果用到的字符存到数组,然后将阿拉伯数字转字符串,遍历拼接结果。


我当时的思路是将整数部分和小数部分分开处理,小数部分比较容易,主要是整数部分比较难处理,整数部分我当时第一想法是从低位往高位遍历,然后按每一位拼接,例如123,则拼出:三、二十、一百,最终组成:一百二十三。


这题思路不难想,方法应该有很多种,但是要能通过测试用例还是挺难的,很多细节需要处理,需要反复调试。



有序列表,找出所有两数之和为10000的组合


思路:可以用 HashMap 来存遍历过的数字,每次遍历时检查下 HashMap 里是否有目标数字,时间复杂度O(n),空间复杂度为O(n)。


如果面试官要求空间复杂度更优的解法,则可以用双指针,从列表的两边向中间扫,和小于10000,则左指针右移,和大于10000,则右指针左移,双指针理论上没有使用额外的空间。



单链表奇数节点移动到最前面


输入:a1->a2->a3->a4->a5

输出:a1->a3->a5->a2->a4


思路:也是硬写,不过这题还是有点规律的,只移动奇数节点,相当于移动一个,跳过一个,移动一个,跳过一个。



字符串的全排列


输入:s = "abc"

输出:["abc","acb","bac","bca","cab","cba"]

思路:回溯法,按顺序固定每一位,同时排除同一位出现重复的情况,剑指 Offer 38 题。



最长上升子序列


输入: [10,9,2,5,3,7,101,18]

输出: 4 ,最长的上升子序列是 [2,3,7,101],它的长度是 4


思路:动态规划,力扣 300 题。



真题:项目、架构、管理


项目介绍


必问的题目,项目介绍主要有两个重点:


1、要让面试官知道你在做什么。可以从项目的背景开始,为什么要做这个项目,按项目的演进过程、分阶段介绍。


2、突出项目的亮点和关键技术。面试官毕竟只是在听一个他没接触过的项目,所以他会更容易去抓取候选人介绍中的一些感兴趣的“关键词语”来提问,例如:从0到1、技术选型、性能优化、表达式引擎、规则引擎、平台化、技术重构、架构升级等等。


当然,你如果提到这些词,就要做好被追问的准备。



技术选型


技术选型主要比较几个维度:性能、稳定性、接入成本、社区活跃度、业务扩展性、业务特点等等,没有所谓最好的框架/工具,只有最合适自己的。


这边拿表达式引擎来作为例子:


我自己选用的是 Aviator,首先我会介绍 Aviator 的基本特点,然后跟 IKExpression、Groovy 进行对比,重点突出 Aviator 的优点:高性能、轻量级,并且强调 Aviator 已经足够满足业务需求,同时其作者在阿里方便进行交流等等。



做过最牛逼的功能


我经常看到有同学吐槽这个问题,但是这个问题说实话问的频率非常高,所以面试前还是得好好准备一下,争取能让面试官眼前一亮。此时不装逼,更待何时。



我这次讲的是一个性能优化的案例,将一个接口从性能提升了两个量级,不看过程只看结果还是有点吓人的。并且讲完案例后,我通常会继续分享一些自性能优化方面的研究和积累。


我们研究和积累的一些方案和技术可能并不适用于自己当前工作的业务场景,但是这些积累可能是很牛逼的,所以这时候你自己要去主动分享出来,让面试官看到你的亮点。



DDD(领域驱动设计)


这个东西最近挺火的,老板们也对这个感兴趣,因为看起来挺高大上的,并且自己也在简历里面写了,所以经常被问,印象中应该是这次面试被问的最多的。


DDD不是一套框架,而是一种架构思想,所以每个人都可以有自己的理解和实践,只要你能讲出你的理解,并且讲出其优点即可。


可以围绕设计思想、核心解决的问题、分层架构、优缺点、项目中的应用去介绍。



平台化


也是老板们比较感兴趣的问题,这个东西就有点涉及到架构方面的知识了。当然,毕竟也5年经验了,这方面的东西确实得有一些了解了。


首先,介绍平台化的业务背景,通常是在一个场景下存在不同的业务模式,两者有区分,但是又有很多共性,所以这个时候去进行平台改造。


同时强调,在从业务模式从一个增加到两个的时候,进行平台化改造是性价比最高的,后续有新的模式就可以直接复用了。


平台化的核心思想:抽取公用逻辑,根据业务的差异点提供自定义扩展点,不同业务模式通过自定义扩展点实现自己的差异化逻辑,以达到抽象复用,提升新业务模式的开发效率。



项目管理经验(PM)


5年经验通常需要在项目或需求中去承担起一个负责人的角色,所以项目管理也是面试官很喜欢考察的。


总结来说,PM 的相关问题都是围绕以下来展开:如何保障项目的按时交付,遇到突发问题时怎么应对。


PM 常见的职责如下:


1)协调各端人员进行需求评审。


2)排期:协调好各端进行排期评估,预留各端人员时间资源,做好总体排期、里程碑设定。


3)约定项目沟通机制:根据项目判断是否需要统一项目室开发、各端之间如何同步进展及问题,如遇项目风险,及时向PM反馈,PM可联合讨论,确定最终方案。


4)把控项目进度:及时了解各端的进度,及时了解项目风险(进度缓慢、人员请假等),及时与产品和业务沟通协调,及时作出风险应对。


5)需求变更控制:项目立项后,所有的需求变更,不得在原需求文档上进行修改。如需修改,需要与PM沟通确认。需求统一交给PM,由PM沟通后邮件通知项目组成员。


6)质量控制:接口提供者,在项目联调前需要保证主体功能OK;各端在联调前做好自测;QA测试时保证环境稳定,不要随意部署。


7)同时作为后端负责人:进行详细的需求分析和拆解,安排好后端人员的开发,做好codereview、方案设计。


8)项目结束后及时进行总结,观察项目目标达成情况,及时进行复盘。



稳定性保障


关于稳定性保障的回答思路可以从“事前预防预警”、“事中快速止损”、“事后复盘总结”去展开。


事前预防预警:稳定性架构设计、防御式编程、业务可降级、力保核心链路、慢SQL/接口优化、日常巡检、灰度发布、业务指标监控、系统指标监控、全链路压测、限流等。


事中快速止损:如果刚上线则快速回滚、及时周知上级及相关业务方、降级开关、限流等,关键思路是先止损而不是纠结于问题。


事后复盘总结:找出问题根源、5whys分析法、制定相应的优化措施、组织团队进行复盘等。



真题:场景题


给出一个场景,问怎么进行整个系统的设计,包括表结构设计,数据的处理等等。


思路:这种题比较考验综合能力,没法提前准备,只能靠平时的积累了。不过场景题的大方向基本是数据量很大,性能优化这些,所以在这方面有积累的同学可能比较容易对付。



面试技巧


合理安排面试顺序


刚开始面试时,一定要先找几家公司练练手,理想中的公司放在后面,面试是需要时间进入状态的,所以切记不要一上来就面自己想进的公司。



保持自信


面试时,很多题目其实是自己刚复习过的,所以此时你对这些题目的理解可能是比面试官更好的,所以一定要自信点,自信有利于自己更好的发挥。



主动引导


面试前我们都会准备一些能让面试官眼前一亮的东西,但是面试时并不一定会被问到,这个时候你得想办法去引导到这个话题上。



最后


目前国内的 Java 环境竞争挺大的,各种内卷,疯狂卷,卷的不可开交,要想全部准备好基本不可能。


所以除了做好面试前的准备,平时日常工作也需要多思考、多总结、多积累,在面试遇到没见过的题目时,才能更从容的应对。


另外,关于算法题,在写本文的时候,我发现自己已经好多算法开始记忆模糊了,好可怕,感觉自己后面得出点算法题文章,加深下印象,哎,真鸡儿难哦。

浏览 54
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报