美团面经来啦~
第一篇文章对自己找实习的经历进行了简单的介绍,也有小伙伴问我有没有美团的面经,既然有问,那么我必须有答!答案是,当然有!而且每一次面试我都会记录下来,今天先来分享美团的一面、二面、HR面。
一面
介绍一下你的项目以及项目问题
你讲一下对于线程池的了解
java线程池大概分为四种:
- newCachedThreadPool
newCachedThreadPool创建一个可缓存线程池,如果当前线程池的长度超过了处理的需要时,它可以灵活的回收空闲的线程,当需要增加时, 它可以灵活的添加新的线程,而不会对池的长度作任何限制。
- newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。定长线程池的大小最好根据系统资源进行设置。
- newScheduledThreadPool
创建一个固定长度的线程池,而且支持定时的以及周期性的任务执行。
- newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它,他必须保证前一项任务执行完毕后才能执行后一项。保证所有任务按照指定顺序执行。
分析线程池的实现原理
- 如果当前运行的线程数小于核心线程数,那么就会新建一个线程来执行任务。
- 如果当前运行的线程数等于或大于核心线程数,但是小于最大线程数,那么就把该任务放入到任务队列里等待执行。
- 如果向任务队列满了,但是当前运行的线程数是小于最大线程数的,就新建一个线程来执行任务。
- 如果当前运行的线程数已经等同于最大线程数了,新建线程将会使当前运行的线程超出最大线程数,那么当前任务会被拒绝,调用拒绝策略方法。
你了解哪些拒绝策略
当时回答:
- AbortStrategy:直接放弃这个任务
- DiscardStrategy:也是直接放弃这个任务,但是他和AbortStrategy稍有不同,但是我忘了有啥不同。
- DiscardOldestStrategy:丢弃任务队列中最老的任务。
正确答案:
- AbortPolicy, 默认
该策略是线程池的默认策略。使用该策略时,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。
- DiscardPolicy
这个策略和AbortPolicy的slient版本,如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常。
- DiscardOldestPolicy
这个策略从字面上也很好理解,丢弃最老的。也就是说如果队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列。 因为队列是队尾进,队头出,所以队头元素是最老的,因此每次都是移除对头元素后再尝试入队。
- CallerRunsPolicy
使用此策略,如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行。
分布式锁使用Redis如何实现?
使用SetNX命令。key为每一种任务类型都对应一个分布式锁,比如简单的打印任务key就是print_lock,值是worker id。expiration设置的是5秒。在释放锁的时候需要判断是不是自己的锁,如果值的ID与自己的ID相同才能释放该锁,以防止将别人的锁给释放了。另外解锁过程中查看、比对和删除键的操作不是原子的。所以将这三个操作放到lua脚本中执行,保证原子性。
你讲一下MySQL数据库的事务
事务有ACID四大特性。就是原子性、一致性、隔离性和持久性。原子性就是指事务中的操作要么全做要么全不做,不存在中间态。一致性是指事务执行前后数据库的完整性不被破坏,保持一致。隔离性是指多个事务并发执行时事务之间互不影响。持久性是指事务执行成功后,事务对于数据库的操作会永久的保存在磁盘上,永不丢失。
你讲一下MySQL数据库的隔离级别?都解决了哪些问题
读未提交,读已提交,可重复读和串行化。
读未提交存在脏读、幻读和不可重复读的问题。
读已提交解决了脏读问题,但是仍然存在幻读和不可重复读问题。
可重复读使用MVCC解决了不可重复读问题,使用MVCC解决了快照读的幻读问题,使用间隙锁解决了当前读的幻读问题。
串行化可以解决所有问题,但是效率比较低。
你这里说到了项目中用到了数据库连接池,你讲一下对它的了解
我主要了解数据库连接池的一些参数。例如:max_connection是连接池最大连接数。还有最大空闲连接数,最小空闲连接数,连接空闲时间等。连接池在刚开始启动时连接池中默认生成最小空闲连接数的连接。如果请求个数超过最小空闲连接数,那么就生成一个新的连接来处理该请求。如果连接数达到最大空闲连接数并且没有超过连接池最大连接数,那么就会新生成一个短连接来处理请求,该短连接在处理完请求之后立即释放,而大于最小空闲小于最大空闲的连接数会等到空闲时间达到配置的连接空闲时间之后才释放。当连接数达到连接池最大连接数时,会拒绝请求。
讲一下使用数据库连接池在多事务并发执行时对于事务有没有什么影响?
假如说一个方法中有三个对于数据库的操作,单线程获取一个数据库连接时,在不开启事务的情况下,一个数据库连接可能对应一个或多个数据库操作,没有直接关系。在开启数据库事务的情况下,方法中的三个操作都会通过同一个数据库连接对数据库进行操作。一个连接也可能处理多个事务。
你说你了解Redis,讲一下
Redis有五种基本数据类型,String、list、set、hash和sorted_set。String底层是用int或者字符串来实现的。list底层在3.2之前使用ziplist或者linkedlist来实现的,3.2之后用的是quicklist来实现的。set底层是用intset或者字典来实现的。hash底层是用ziplist或者字典来实现的。sorted_set底层是用ziplist或者字典+跳表来实现的。
这里我就大概说了一些,说多了面试官也不怎么听,你可以说一些然后接下来等面试官问你。
操作系统各种的死锁你了解吗,讲一下
当两个或者以上线程并行执行的时候,争夺资源而造成相互等待的现象,这时候就是发生了死锁。
死锁产生的四个必要条件:
- 互斥条件:一个资源一次只能被一个进程使用
- 持有并等待条件:一个进程因请求资源而阻塞时,对已获得资源保持不放
- 不剥夺条件:进程获得的资源,在未完全使用完之前,不能强行剥夺
- 环路等待条件:若干进程之间形成一种头尾相接的环形等待资源关系
Spring你了解哪些?
Spring的核心是IOC和AOP。
IoC(控制反转) 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。
AOP(面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,以减少系统的重复代码,降低模块间的耦合度。Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。
接下来又问了两个项目问题。。。然后就是算法了
算法
- 面试官自命题,测试题:判断一个字符串是否符合以下条件:字符串中只包含小写英文字母,并且长度不超过15。字符串中包含其他字符时,长度不限字符串中出现“,”“、”“。”时直接返回false。
- 查看一个链表是否有环
- 查找有环链表的入口节点
反问
- 我面的行吗(弱弱的问了一句,这个问题最好不要问)
- 部门使用什么语言
- 部门是做什么业务的
- 一面结果几天能出(这个最好也不要问)
面试总结
- 拒绝策略记混了,而且记得不全
- 遇到不会的问题一点思路都没有
- 有点太激动了,问了几个敏感的问题
二面
一开始一半的时间都在问项目
Java内存模型
JVM内存结构分为5大区域,程序计数器、虚拟机栈、本地方法栈、堆内存、方法区。
方法区:
用来存储加载的类信息、常量、静态变量、编译后的代码等数据。
堆内存:
堆内存可以细分为:老年代、新生代(Eden、From Survivor、To Survivor)。JVM启动时创建,用来存放对象的实例。堆内存是垃圾收集器管理的主要区域。
虚拟机栈:
线程私有的。虚拟机栈由多个栈帧组成。一个线程会执行一个或多个方法,一个方法对应一个栈帧。每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出。栈帧内容包含:局部变量表、操作数栈、动态链接、方法返回地址等信息。
本地方法栈:
和虚拟机栈功能类似,虚拟机栈是为虚拟机执行JAVA方法而准备的,本地方法栈是为虚拟机使用Native本地方法而准备的。
程序计数器(Program CounterRegister):
线程私有的,程序计数器主要有两个作用:
- 作为当前线程所执行的字节码的行号指示器,通过它实现代码的流程控制,如:顺序执行、分支、循环、异常处理。
- 在多线程的情况下,程序计数器用于记录当前线程执行的位置,当线程被切换回来的时候可以通过程序计数器中的信息获取上次执行的位置,然后继续执行。
哪些区域可能会发生full GC?
堆内存。剩下的我就不太了解了。但是我知道哪些区域可能会发生OOM问题。
哪些区域会发生OOM问题?方法区在什么情况下会发生OOM?
除了程序计数器外的其他四个区域都会。
这个我不太清楚。这里纯纯是给自己挖了一个坑。
什么情况下会发生full GC?
- 程序调用System.gc()的时候。
- 老年代空间不够用的时候。
我就知道这些。
补充:还有当大对象来到老年代,老年代没有连续的空闲空间容纳大对象时
永久代或者元数据空间不足时
为什么说full GC效率比较低?为什么full GC的时候用户程序不能运行?
因为full GC的时候需要stop the world。会阻塞程序的执行。
因为在full GC的过程中运行程序的话,运行的程序还可能会产生垃圾。
你知道Java的内存重排序吧,讲一下
就是编译器为了优化执行效率可能会对指令重新排序。比如我们程序中输入a=1,b=2。实际执行的时候可能是b=2,a=1。
如何防止指令重排序?
内存屏障
讲一下关键字如何实现指令重排序
volatile规定volatile写之前的操作不能被重排序到volatile写之后。volatile读操作之后的操作不会被重排序到volatile读操作之前。
MySQL索引
有一个联合索引(a,b,c)。问:
- Where a = x and c = x 会使用到索引吗? 只会使用到索引a,不会使用其他索引
- Where b= x and a = x and c = x 会使用到索引吗? 不会使用到索引,这里回答掉进坑里面了,MySQL优化器会对条件进行重排序,实际上a,b,c都会走索引
平时有查询过服务器日志吗?熟悉linux命令吗?查找文件中的固定字段使用哪个命令?
平时没怎么看过服务器日志。使用grep命令。
你说你本科和硕士学习成绩都比较优异,说一说你觉得你身上什么优点让你比他们更优异?你有哪些缺点?举例说明
- 比较刻苦,能干。
- 缺点:性子比较急。
算法
- 面试官自命题,输入一个五位数,返回一个中文字符串,比如12345,“一万两千三百四十五”。
反问
- 部门是做什么业务的,上次没有太听懂?
- 我要是通过的话能早点过去实习嘛,我想多实习一段时间
- 进去之后有人带吗
面试总结
感觉
有两三个Java的问题感觉答得不是特别好,还给自己挖了个坑。引以为戒。
不足之处
- 继续补充八股文
HR面
美团的HR面是直接告诉我薪资和补贴,剩下问问有哪个公司的offer就没了。
#你的美团面试都问了啥#