美团到家面试,过了!

440次阅读  |  发布于5月以前

虽然现在秋招快到了 12 月份,但是其实还是有补录的阶段,最近好几位同学跟我反馈在最后的阶段速通的 offer,很容易就捡漏 offer 了。

其实最怕你自己觉得自己没机会,停止了行动,这时候其实是你自己放弃了机会,希望同学们持续坚持学+坚持投,别内耗自己,找工作本来就是持久战的过程。

今天分享美团Java后端面经,考察的范围还挺多的,计算机基础+mysql+redis+mq+java并发+java 集合+jvm这些方面都进行盘问了。

面经比较有代表性,也对问题做了总结,希望能帮助最近在准备面试的同学。根据面试热点题目去准备知识的,目的性会比较强,方向也比较清晰一点。

操作系统

死锁的条件有哪些?

死锁只有同时满足以下四个条件才会发生:

避免死锁问题就只需要破环其中一个条件就可以,最常见的并且可行的就是使用资源有序分配法,来破环环路等待条件

那什么是资源有序分配法呢?线程 A 和 线程 B 获取资源的顺序要一样,当线程 A 是先尝试获取资源 A,然后尝试获取资源 B 的时候,线程 B 同样也是先尝试获取资源 A,然后尝试获取资源 B。也就是说,线程 A 和 线程 B 总是以相同的顺序申请自己想要的资源。

乐观锁和悲观锁及使用场景?

乐观锁:

悲观锁:

乐观锁适用于读多写少的场景,通过版本控制来处理冲突;而悲观锁适用于写多的场景,通过加锁来避免冲突。

Redis

redis应用场景有哪些?

我们直接看 Redis 官方是怎么介绍自己的。

imgRedis 官方的介绍原版是英文的,我翻译成了中文后截图的,所以有些文字读起来会比较拗口,没关系,我会把里面比较重要的特性抽出来讲一下。

Redis 是一种基于内存的数据库,对数据的读写操作都是在内存中完成,因此读写速度非常快,常用于缓存,消息队列、分布式锁等场景

Redis 提供了多种数据类型来支持不同的业务场景,比如 String(字符串)、Hash(哈希)、 List (列表)、Set(集合)、Zset(有序集合)、Bitmaps(位图)、HyperLogLog(基数统计)、GEO(地理信息)、Stream(流),并且对数据类型的操作都是原子性的,因为执行命令由单线程负责的,不存在并发竞争的问题。

除此之外,Redis 还支持事务 、持久化、Lua 脚本、多种集群方案(主从复制模式、哨兵模式、切片机群模式)、发布/订阅模式,内存淘汰机制、过期删除机制等等。

redis数据结构有那些?

Redis 五种数据类型的应用场景:

zset使用场景?

Zset 类型(Sorted Set,有序集合) 可以根据元素的权重来排序,我们可以自己来决定每个元素的权重值。比如说,我们可以根据元素插入 Sorted Set 的时间确定权重值,先插入的元素权重小,后插入的元素权重大。

在面对需要展示最新列表、排行榜等场景时,如果数据更新频繁或者需要分页显示,可以优先考虑使用 Sorted Set。

排行榜

有序集合比较典型的使用场景就是排行榜。例如学生成绩的排名榜、游戏积分排行榜、视频播放排名、电商系统中商品的销量排名等。

我们以博文点赞排名为例,小林发表了五篇博文,分别获得赞为 200、40、100、50、150。

# arcticle:1 文章获得了200个赞
> ZADD user:xiaolin:ranking 200 arcticle:1
(integer) 1
# arcticle:2 文章获得了40个赞
> ZADD user:xiaolin:ranking 40 arcticle:2
(integer) 1
# arcticle:3 文章获得了100个赞
> ZADD user:xiaolin:ranking 100 arcticle:3
(integer) 1
# arcticle:4 文章获得了50个赞
> ZADD user:xiaolin:ranking 50 arcticle:4
(integer) 1
# arcticle:5 文章获得了150个赞
> ZADD user:xiaolin:ranking 150 arcticle:5
(integer) 1

文章 arcticle:4 新增一个赞,可以使用 ZINCRBY 命令(为有序集合key中元素member的分值加上increment):

> ZINCRBY user:xiaolin:ranking 1 arcticle:4
"51"

查看某篇文章的赞数,可以使用 ZSCORE 命令(返回有序集合key中元素个数):

> ZSCORE user:xiaolin:ranking arcticle:4
"50"

获取小林文章赞数最多的 3 篇文章,可以使用 ZREVRANGE 命令(倒序获取有序集合 key 从start下标到stop下标的元素):

# WITHSCORES 表示把 score 也显示出来
> ZREVRANGE user:xiaolin:ranking 0 2 WITHSCORES
1) "arcticle:1"
2) "200"
3) "arcticle:5"
4) "150"
5) "arcticle:3"
6) "100"

获取小林 100 赞到 200 赞的文章,可以使用 ZRANGEBYSCORE 命令(返回有序集合中指定分数区间内的成员,分数由低到高排序):

> ZRANGEBYSCORE user:xiaolin:ranking 100 200 WITHSCORES
1) "arcticle:3"
2) "100"
3) "arcticle:5"
4) "150"
5) "arcticle:1"
6) "200"

zset底层是怎么实现的?

Zset 类型的底层数据结构是由压缩列表或跳表实现的:

在 Redis 7.0 中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。

跳表时间复杂度?

MySQL

左连接和右链接的区别?

上图的 left Jon 是左连接,right join 是右连接:

mysql有哪些引擎,区别?

MySQL中常用的四种存储引擎分别是:MyISAM存储引擎、innoDB存储引擎、MEMORY存储引擎,区别如下:

怎么优化一个慢SQL?

explain的结果有哪些?有哪些信息去告诉你怎么优化?

对于执行计划,参数有:

type

type 字段就是描述了找到所需数据时使用的扫描方式是什么,常见扫描类型的执行效率从低到高的顺序为

在这些情况里,all 是最坏的情况,因为采用了全表扫描的方式。index 和 all 差不多,只不过 index 对索引表进行全扫描,这样做的好处是不再需要对数据进行排序,但是开销依然很大。所以,要尽量避免全表扫描和全索引扫描。

range 表示采用了索引范围扫描,一般在 where 子句中使用 < 、>、in、between 等关键词,只检索给定范围的行,属于范围查找。从这一级别开始,索引的作用会越来越明显,因此我们需要尽量让 SQL 查询可以使用到 range 这一级别及以上的 type 访问方式

ref 类型表示采用了非唯一索引,或者是唯一索引的非唯一性前缀,返回数据返回可能是多条。因为虽然使用了索引,但该索引列的值并不唯一,有重复。这样即使使用索引快速查找到了第一条数据,仍然不能停止,要进行目标值附近的小范围扫描。但它的好处是它并不需要扫全表,因为索引是有序的,即便有重复值,也是在一个非常小的范围内扫描。

eq_ref 类型是使用主键或唯一索引时产生的访问方式,通常使用在多表联查中。比如,对两张表进行联查,关联条件是两张表的 user_id 相等,且 user_id 是唯一索引,那么使用 EXPLAIN 进行执行计划查看的时候,type 就会显示 eq_ref。

const 类型表示使用了主键或者唯一索引与常量值进行比较,比如 select name from product where id=1。

需要说明的是 const 类型和 eq_ref 都使用了主键或唯一索引,不过这两个类型有所区别,const 是与常量进行比较,查询效率会更快,而 eq_ref 通常用于多表联查中

除了关注 type,我们也要关注 extra 显示的结果。

这里说几个重要的参考指标:

消息队列

如何避免mq重复消费?

导致重复消费的原因可能出现在生产者,也可能出现在 MQ 或 消费者。

这里说的重复消费问题是指同一个数据被执行了两次,不单单指 MQ 中一条消息被消费了两次,也可能是 MQ 中存在两条一模一样的消费。

消费者怎么解决重复消费问题呢?这里提供两种方法:

网络

token,session,cookie的区别?

tcp与udp的区别

Java

synchronized 和 ReentrantLock 区别?

synchronized 和 ReentrantLock 都是 Java 中提供的可重入锁:

Spring的生命周期说一下?

Spring的生命周期大致分为:创建 -> 属性填充 -> 初始化bean -> 使用 -> 销毁 几个核心阶段:

说一下JVM加载一个类的过程

JVM 中类的装载是由类加载器,也就是ClassLoader,和它的子类来实现的,Java 中的类加载器是一个重要的 Java 运行时系统组件,它负责在运行时查找和装入类文件中的类。

由于 Java 的跨平台性, 经过编译的 Java 源程序并不是一个可执行程序, 而是一个或多个类文件。当 Java 程序需要使用某个类时,JVM 会确保这个类已经被加载、连接( 验证、 准备和解析)和初始化。

类的加载是指把类的.class 文件中的数据读入到内存中,通常是创建一个字节数组读入.class 文件,然后产生与所加载类对应的 Class 对象。加载完成后, Class 对象还不完整, 所以此时的类还不可用。当类被加载后就进入连接阶段, 这一阶段包括验证、准备( 为静态变量分配内存并设置默认的初始值) 和解析( 将符号引用替换为直接引用) 三个步骤。

最后 JVM 对类进行初始化,包括:1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;2)如果类中存在初始化语句, 就依次执行这些初始化语句。

垃圾收集有哪些算法?

String s = new String(“abc”)执行过程中分别对应哪些内存区域?

首先,我们看到这个代码中有一个new关键字,我们知道new指令是创建一个类的实例对象并完成加载初始化的,因此这个字符串对象是在运行期才能确定的,创建的字符串对象是在堆内存上

其次,在String的构造方法中传递了一个字符串abc,由于这里的abc是被final修饰的属性,所以它是一个字符串常量。在首次构建这个对象时,JVM拿字面量"abc"去字符串常量池试图获取其对应String对象的引用。于是在堆中创建了一个"abc"的String对象,并将其引用保存到字符串常量池中,然后返回;

所以,如果abc这个字符串常量不存在,则创建两个对象,分别是abc这个字符串常量,以及new String这个实例对象。如果abc这字符串常量存在,则只会创建一个对象

Hashmap原理?1.7 和 1.8 有什么区别?

图片

// JDK 1.7 hash 方法源码.
final int hash(Object k) {
    int h = hashSeed;
    if (0 != h && k instanceof String) {
        return sun.misc.Hashing.stringHash32((String) k);
    }

    h ^= k.hashCode();

    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}


// JDK 1.8 hash 方法源码.
static final int hash(Object key) {
      int h;
      // key.hashCode():返回散列值也就是hashcode
      // ^:按位异或
      // >>>:无符号右移,忽略符号位,空位都以0补齐
      return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  }

了解的哈希冲突解决方法有哪些?

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8