美团
首先上来介绍自己
这里没什么说的,硬要说坑的话就是突出说了粮储那个项目,其实简历上Redis这块也蛮坑的,说实话有时间应该系统的学习下Redis了,不能每次问到都不会了。
问项目
因为前阵子看数据库看的比较多,所以一直把面试官往这里引导,不过问粮储的原因导致这里准备的不是很充分,索引加的有点莫名其妙,总结下来应该多看看索引的实际应用这里。感觉我应该说在公司名称上加索引要更好。
索引的原理
这里主要问了索引为什么使用B+树,感觉自己答得不是很好,首先B+树拥有B树的全部优点,每个节点增加了多个子节点,每个page可以容纳更多的节点,导致减少I/O次数,增加了性能。不同的是B+树的数据只在叶子节点存储,这样每个节点相比于B树更小,从而使树更浅。其次扫库扫表的能力更强,因为叶子节点是双向指针,我们可以从叶子节点开始查询,无需回到根节点重新查询。在排序与范围查找能力更强,因为其可以直接在叶子节点进行查询。
InnoDB有没有非聚集索引
这个答得太草率了,其实应该是有的,因为想到辅助索引存的是主键值,所以觉得没有,其实是错的。InnoDb里非聚簇索引都是辅助,像联合索引、前缀索引、唯一索引等都是非聚簇索引。
算法:遍历二叉树
这个其实应该写上的,回溯的想法是正确的,缺点是不该写循环,直接一条道遍历下去就好了。
线程池配置参数
因为线程池没好好看,这道题就答上了一个核心线程数和最大线程数。这里做个补充。有一个interface的阻塞队列,可由用户定制,有一个Set类型的workers,用来存储工作线程的集合,一个ReetrantLock类型的mainLock,一个线程池总共处理的任务数,一个最多的活跃线程数,还有拒绝策略。
了解的锁
自旋锁、偏向锁、公平锁、非公平锁、可重入锁、共享锁、排它锁、轻量级锁(轻量级锁可以看成重量级锁的退化版本,不使线程阻塞,而是采用自旋)
自旋锁的原理
CAS这个背的比较熟了
有哪些锁使用了CAS
AQS实现的锁都使用了CAS,还包括轻量级锁。
Redis可以存哪些类型
string、list、hash、set、sorted set
Redis底层数据结构
链表、双端链表、字典、整数集合、跳跃表、long类型的整数、简单动态字符串、压缩列表
项目为什么使用SpringBoot
其实就是问SpringBoot的优点 (1) Spring Boot使编码变简单 (2) Spring Boot使配置变简单 (3) Spring Boot使部署变简单 (4) Spring Boot使监控变简单
SpringMVC是怎么进行前后台连接的
- 发送请求到前端控制器(DispatcherServlet)
- 前端控制器请求HandlerMapping查找Handler(可以根据xml配置、注解进行查找)
- 处理器映射器HandlerMapping向前端控制器返回Handler
- 前端控制器调用处理器适配器去执行Handler
- 处理器适配器执行Handler
- Handler执行完给处理器适配器返回modelAndView
- 处理器适配器向前端控制器返回ModelAndView(ModelAndVIew是Springmvc底层的对象,包括Model、View)
- 前端控制器请求视图解析器去进行视图解析(根据逻辑视图名去解析真正的视图(jsp))
- 视图解析器向前端控制器返回view
- 前端控制器进行试图渲染(试图渲染:将模型数据(modelAndView对象中)填充到request域)
- 前端控制器向用户响应结果
你是如何学习Java的
这里答得是看书,看视频。思考了一下应该把写博客和研究github代码也加入进去,同时不能忘了动手操作。
美团优选
1.自我介绍
这次的自我介绍我觉得还是挺满意的,不过美中不足的是可能是没有详细的说明项目,导致根本没有问到有关项目的知识。
2.算法:二叉树的最近公共祖先
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || p == root || q == root) {
return root;
}
TreeNode pqInLeft = lowestCommonAncestor(root.left, p, q);
TreeNode pqInRight = lowestCommonAncestor(root.right, p, q);
if(pqInLeft == null) {
return pqInRight;
}
if(pqInRight == null) {
return pqInLeft;
}
return root;
}
}
3.用Linux写出一个寻找重复数的代码
这方面是真的积累少了,答完这个我就把简历里Linux的内容去掉了…下面是在网上找的内容,这里做一下积累: Linux uniq 命令用于检查及删除文本文件中重复出现的行列,一般与 sort 命令结合使用。
uniq 可检查文本文件中重复出现的行列。
语法 uniq [-cdu][-f<栏位>][-s<字符位置>][-w<字符位置>][–help][–version][输入文件][输出文件] 参数:
- c或–count 在每列旁边显示该行重复出现的次数。
- d或–repeated 仅显示重复出现的行列。
- f<栏位>或–skip-fields=<栏位> 忽略比较指定的栏位。
- s<字符位置>或–skip-chars=<字符位置> 忽略比较指定的字符。
- u或–unique 仅显示出一次的行列。
- w<字符位置>或–check-chars=<字符位置> 指定要比较的字符。
- -help 显示帮助。
- -version 显示版本信息。
- [输入文件] 指定已排序好的文本文件。如果不指定此项,则从标准读取数据;
- [输出文件] 指定输出的文件。如果不指定此选项,则将内容显示到标准输出设备(显示终端)。
3.1 算法:寻找数组中的重复数
class Solution {
public int findRepeatNumber(int[] nums) {
HashSet set = new HashSet<>();
for(int i = 0; i < nums.length; i++) {
if(!set.contains(nums[i])) {
set.add(nums[i]);
} else {
return nums[i];
}
}
return -1;
}
}
3.1.1 用HashMap的算法复杂度呢?
因为上道题我用的HashMap,所以就有了这道题。很简单:O(n)。
3.2 如果这个重复数的文件超出内存怎么办?
分批处理,这里做一个积累: https://blog.csdn.net/liaonanfeng88/article/details/103629506
4.场景题:转账
用事务
5.事务的隔离等级
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
不可重复读(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
6.可重复读的原理
这个参考自己的文章就行了,不过还没更新到= =
7.说一下ThreadPoolExecutor的成员变量
这个参考我的博文 http://182.92.213.85/articles/44
8.说一下工作队列,最大线程数,核心线程数
这个参考同样我的博文 http://182.92.213.85/articles/44
8.1工作队列满了是直接申请到最大线程数么?
8.2 你觉得核心线程数和最大线程数之间应该如何设置呢
这个根本没准备,所幸就用25%了,然后说是因为HashMap的启发,但事实肯定不是这样的。
对于CPU密集型任务,由于CPU密集型任务的性质,导致CPU的使用率很高,如果线程池中的核心线程数量过多,会增加上下文切换的次数,带来额外的开销。因此,一般情况下线程池的核心线程数量等于CPU核心数+1。(注:这里核心线程数不是等于CPU核心数,是因为考虑CPU密集型任务由于某些原因而暂停,此时有额外的线程能确保CPU这个时刻不会浪费。但同时也会增加一个CPU上下文切换,因此核心线程数是等于CPU核心数?还是CPU核心数+1?可以根据实际情况来确定)
对于I/O密集型任务,由于I/O密集型任务CPU使用率并不和很高,可以让CPU在等待I/O操作的时去处理别的任务,充分利用CPU。因此,一般情况下线程的核心线程数等于2*CPU核心数。(注:有些公司会考虑所需要的CPU阻塞系数,即核心线程数=CPU核心数/(1-阻塞系数))
对于混合型任务,由于包含2种类型的任务,故混合型任务的线程数与线程时间有关。一般情况下:线程池的核心线程数=(线程等待时间/线程CPU时间+1)*CPU核心数;在某种特定的情况下还可以将任务分为I/O密集型任务和CPU密集型任务,分别让不同的线程池去处理,但有一个前提–分开后2种任务的执行时间相差不太大。
8.3 HashMap的扩容机制
这个不久就会更新的
8.3.1 HashMap的原理
同上
9. 垃圾回收算法
- Mark-Sweep(标记-清除)算法
- Copying(复制)算法
- Mark-Compact(标记-整理)算法(压缩法)
- Generational Collection(分代收集)算法
10. 如何判断对象需要回收?
GC-root(这里问我对象调用是怎么回事,是调用方法么?我说是,但感觉有问题,这里我会去查查,查到了就会更新)
11. JVM用到了什么垃圾回收算法
目前大部分垃圾收集器对于新生代都采取复制算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。
而由于老年代的特点是每次回收都只回收少量对象,一般使用的是标记-整理算法(压缩法)。