前言
在这个金三银四的黄金季,献给大伙奉上一套热腾腾的大厂面试题,来自朋友的面试经历(2面技术+1面hr),由于朋友还没入职,不让我透露具体公司,但是绝对是大厂不假(想知道可以先下偷偷告诉你,或者让朋友给你内推),笔者收集整理了一下,废话不多说,直接进入正题。
以下给出了每一题思路及方向,后续还需要更多资料的小伙伴们看文末,希望能给你们一下帮助。
技术一面——考察知识深度
java基础篇
1、有哪些集合实现,对应的线程安全集合?
主要考察集合框架的两大类:集合(collection)和图(map),集合下面主要了解list和set,如ArrayList、LinkedList、HashSet、TreeSet;map下主要了解HashMap、TreeMap。
划重点(加分项):
① 线程安全集合选两个代表性的:ConcurrentHashMap和CopyOnWriteArrayList,了解它们是如何做到线程安全的。
2、hashmap底层结构,为什么使用链表?(hashmap是面试必考题)
HashMap的底层结构(数组)实现,一定要了解透彻,如put()、get()两块源码花点心思;
hash碰撞后产生的链表、红黑树顺带了解,有能力的最好看下源码实现(加分项)。
3、线程池常用参数、如何设置?
核心线程数、最大线程数、阻塞队列容量、线程空闲时间及单位、拒绝策略,每个的具体含义一定要了解。
划重点:
当线程数小于核心线程数时,创建线程。
当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
当线程数大于等于核心线程数,且任务队列已满:
若线程数小于最大线程数,创建线程若线程数等于最大线程数,看拒绝策略
如何设置具体参数值(个人经验及看法,供参考):
核心线程数 = 每秒需要处理的最大任务数量 * 处理一个任务需要的时间,业界有常用的8020原则、6040原则,实际还是要根据自己应用的承载能力来看,假设系统每秒任务数为10~100,每个任务耗时0.1秒,则需要10*0.1~100*0.1,即1~10个线程。那么corePoolSize应该设置为大于1,若根据8020原则,即80%情况下系统每秒任务数不超过20,则corePoolSize=0.1*20=2。
阻塞队列容量 = 核心线程数 / 处理一个任务需要的时间 * 系统允许任务最大的响应时间
最大线程数 = (每秒需要处理的最大任务数量 - 队列容量)*每秒的线程处理任务能力,假设每秒200个任务需要20个线程,那么当每秒达到1000个任务时,则需要(1000-队列容量)*(20/200),即60个线程。
4、阻塞队列的实现原理?
lock锁的多条件(condition)阻塞控制,基础薄弱的需要提前了解一下同步锁synchronized、lock及Condition类,这一块考验高级和中级研发人员的界限,需要挖一下源码,多用心。
加分项:另外辐射出来可能会顺带直接问你AQS,建议仔细了解其实现原理,如其内部如何利用双向链表的,重点看acquire()方法实现。
JVM篇
1、内存模型(必考题)
无论你关注jdk1.8之前还是之后,不重要,关键是能正常理解每一块的作用、是否线程共享/私有。
堆、虚拟机栈、本地方法栈、程序计数器、方法区,需要了解每一部分的作用。
划重点:
虚拟机栈包含哪些部分、程序计数器的作用这两块着重了解。
2、GC发生在哪一块,GC算法?
发生在堆内存,GC算法主要了解:标记清除、复制、标记整理,需要知道其执行步骤及各自的区别。
划重点:
年轻代划分成哪几部分(E、S0、S1),使用哪种算法(复制),老年代使用哪种算法(标记清除),为什么?这里需要耐心理解透彻,很容易辐射到此类问题。
MySql篇
1、事务隔离级别,默认哪一个,解释幻读、不可重复读及二者区别?
隔离级别:读未提交、读提交、不可重复读、串行化,默认是不可重复读,具体幻读问题,请参考笔者此前文章幻读、不可重复读,其中进行了详细说明。
2、索引失效场景、索引数据结构?
主要分为非组合索引和组合索引:
1、非组合索引主要注意or、like(若like非左模糊情形,如xxx%,则可以使用索引),索引列存在表达式、聚合函数等等;
2、组合索引主要注意是否遵循最左原则。
还有些其它小点靠大家自己去积累,笔者这会是蒙圈状态,没反应过来。
索引数据结构:主要关注B+树,花心思了解透,且需要了解聚簇索引和非聚簇索引。
加分项:InnoDB与MyisAM索引方面的区别。
3、数据库引擎
主要可以说说InnoDB、MyISAM这两个常用的即可,需要了解二者的区别,InnoDB支持事务,二者使用场景,二者之间如何转变。
4、mysql分表中间件、如何监听binlog
中间件:tddl,MyCat(其实考验你的知识储备广度,一般不要求细说中间件的实现)。
监听时可以使用duckula中间件或者binlog监听独立jar包(common-binlog-alone)。
5、笔者认为高级或资深人员必须要了解的知识储备:InnoDB的MVCC机制
mysql自身的并发控制如何实现的,原理主要从其新增、更新、删除三方面去了解其内部的操作过程,其版本号如何控制等等。
中间件篇
1、消息队列用过哪些:kafka、rabbitMq、rocketMq?
以kafka为例,一定要能手绘kafka的逻辑结构图,知识点主要被问到两方面:
零拷贝:磁盘文件的数据复制到页面缓存中,然后将数据从页面缓存直接发送到网络中,不用重复从磁盘取数据。
kafka延时队列怎么实现:时间轮,需要细致了解其如何将消息遍布其中并且延时取出。
怎么保证消息有序:这个有序的前提是在partition层面,其中的消息在写入时都是有序的,消费时,每个partition只能被每一个group中的一个消费者消费,进而保证了其有序性。
kafka如何保证消息高可用:着重去了解ISR机制和复制机制,很难细说清楚,需要花时间了解。
kafka的事务是如何实现:三两句是说不清的,主要从幂等性发送和事务性保证两方面来回答,具体给几个小点供参考(1、引入内部Topic作为事务log;2、引入transactionId做同一事务判断;3、引入事务协调者;4、引入控制消息让broker通知消费者消息是否被原子性提交,并对使用者透明,不可见),参考点并不完全,需要读者用心自行完善。
加分项:
若使用过不止一种消息队列,最好能知晓其之间的差异或者优劣势。
2、搜索中间件使用过哪些,ES?
ES索引结构:index/type/id,类比关系型数据库:index=database,type=table,id=id。
ES分片:主要了解主分片(不可修改)与副分片(可以修改),主分片与对应的副分片不能存放同一个节点下,查询时主、副都是查询,es会择优返回结果。另外,分片路由算法了解一下:分片=hash(routing)%主分片数,routing一般可以使用id。
笔者建议还要了解下ES中的删除其实不是物理删除,具体如何删的有兴趣可以自行了解,属于加分项
3、缓存使用过什么,redis?
redis常用数据类型:String、List、hash、set、zset等等,使用场景最好也事先想清楚,100%会被问到。
redis集群架构模式:哨兵模式、主从模式、cluster模式,具体原理需要细致了解,大概率会被问到。
redis如何实现延时消息:此题考验临时给定场景的解决问题能力,其实笔者还是觉得考察的是对redis数据类型的熟悉程度。笔者给的思路是使用zset类型,将延时的时间点作为score进行存放,使用时启用一个轮询任务按照允许的时间间隔进行轮询取出一定量的数据,使用zrangeByScore方法根据score排好序的结果,使用完记得zrem删除避免重复消费,当然其中需要你控制好如何在删除之前避免并发的重复消费。
技术二面——考察实际问题解决能力
1、对于分表之后的业务常用数据,比如订单分表后,要查某段时间内的订单数据,如何实现?
本题不能直接将一张张分表查询一遍,效率太低,偏离实际使用场景的时效性和性能,所以考虑引入中间件(比如ES),可以将业务热度较高的数据抽取出来按照用户常用查询维度进行组合存放,当实际请求过来时,直接通过es即可获取相应结果。
注:这种方式也适用于DB的减压,若问到DB如何减压也可以这么回答。
2、比如下单之后有很多相应的数据都要同步修改,但是这类数据都不在同一个应用服务和数据库中,有什么方式可以达到数据的最终一致性?
这一题笔者给出两种思路(笔者倾向于后一种方式):
①使用消息,不同数据对应其所在的服务进行消息监听,进而获取消息后进一步修改,当然不能排除消息丢失的情况,所以需要进一步完善;
②可以监听binlog,当下单数据变更后,直接通过监听到的binlog的数据变更结果之后,根据具体需要进行对应的后续处理。
3、cup飙升如何排查?
具体说下思路:
① 先要ps -ef | grep java获得java进程,然后使用top命令获取cpu使用较高的线程;
② 使用jstack -l pid > /tmp/xxxx.log将堆栈情况输出到文件中便于后续查看;
③将top中cpu使用率较高的线程id转换成16进制去上一步的文件中查找,大多数情况下可以定位出一些眉目;
④如果仍然没有头绪,接着使用jmap导出堆的dump文件,并使用Eclipse的插件进行查看,比如找到问题对象进行实际代码分析。
现场编程
1、将单链表逆序输出,不能改变单链表的结构(比如不允许将单链表改造成双向链表)
这一题笔者给出两个思路:
① 简单点考虑可以借助栈来实现,顺序压栈后直接栈顶顺输出即可;
② 当然,如果要求不允许借助栈来实现,那就需要递归的思想,设置两个指针变量a和b(a->b),分别指向表头前两个节点,使得a指向的节点为b指向的节点的后继,再利用一个临时变量,逐步后移a、b直到b为空即可。
2、电影院选座位,共n个座位,每个座位票价不一样,找出连续的m个座位,使其票价总和最小。
这一题笔者直接理解为那个元素的数组(元素都是数值),找出m个连续的数字使其总和最小,给出起始点的下标即可。
可以直接顺序查找,每次计算出的m个元素的总和值保存在临时变量中,例如若使用map,则key为下标,value为sum值,记住不要过多浪费空间,并不需要每个下标都存放进去,只要存放一个即可,然后依次遍历数组下标,比较sum值(较小的放进map,同时为了保证节省空间,放进map前进行清空map,使map始终只有一个元素),最终能获取到最小的sum,拿出map的为一个键值对,即获取到起始下标。
一般现场编程题不会太难,毕竟不是让你现场ACM,而是着重考察面试者是否保留了过硬的动手能力,而不是只会CURD,但是算法还是需要日常积累才能自如应付。
更多资料:
感兴趣且需要的小伙伴看文末领取即可