关于java:美团面试真题和答案

70次阅读

共计 6263 个字符,预计需要花费 16 分钟才能阅读完成。

问题来源于某客,如下图所示:

问题链接:https://www.nowcoder.com/feed/main/detail/b12f8ece42f6485d8e462ab872c4f8d8

答案解析

1. 线程池有几种实现形式?

线程池的创立办法总共有 7 种,但总体来说可分为 2 类:

  1. 通过 ThreadPoolExecutor 创立的线程池;
  2. 通过 Executors 创立的线程池。

线程池的创立形式总共蕴含以下 7 种(其中 6 种是通过 Executors 创立的,1 种是通过 ThreadPoolExecutor 创立的):

  1. Executors.newFixedThreadPool:创立一个固定大小的线程池,可管制并发的线程数,超出的线程会在队列中期待;
  2. Executors.newCachedThreadPool:创立一个可缓存的线程池,若线程数超过解决所需,缓存一段时间后会回收,若线程数不够,则新建线程;
  3. Executors.newSingleThreadExecutor:创立单个线程数的线程池,它能够保障先进先出的执行程序;
  4. Executors.newScheduledThreadPool:创立一个能够执行提早工作的线程池;
  5. Executors.newSingleThreadScheduledExecutor:创立一个单线程的能够执行提早工作的线程池;
  6. Executors.newWorkStealingPool:创立一个抢占式执行的线程池(工作执行程序不确定)【JDK 1.8 增加】。
  7. ThreadPoolExecutor:最原始的创立线程池的形式,它蕴含了 7 个参数可供设置,会更加可控。

    2. 线程池的参数含意?

    问到线程池参数的含意,肯定是问 ThreadPoolExecutor 参数的含意,这七个参数的含意别离是:

  8. 个参数代表的含意如下:

    参数 1:corePoolSize

    外围线程数,线程池中始终存活的线程数。

    参数 2:maximumPoolSize

    最大线程数,线程池中容许的最大线程数,当线程池的工作队列满了之后能够创立的最大线程数。

    参数 3:keepAliveTime

    最大线程数能够存活的工夫,当线程中没有工作执行时,最大线程就会销毁一部分,最终放弃外围线程数量的线程。

    参数 4:unit:

    单位是和参数 3 存活工夫配合应用的,合在一起用于设定线程的存活工夫,参数 keepAliveTime 的工夫单位有以下 7 种可选:

  • TimeUnit.DAYS:天
  • TimeUnit.HOURS:小时
  • TimeUnit.MINUTES:分
  • TimeUnit.SECONDS:秒
  • TimeUnit.MILLISECONDS:毫秒
  • TimeUnit.MICROSECONDS:奥妙
  • TimeUnit.NANOSECONDS:纳秒

    参数 5:workQueue

    一个阻塞队列,用来存储线程池期待执行的工作,均为线程平安,它蕴含以下 7 种类型:

  • ArrayBlockingQueue:一个由数组构造组成的有界阻塞队列;
  • LinkedBlockingQueue:一个由链表构造组成的有界阻塞队列;
  • SynchronousQueue:一个不存储元素的阻塞队列,即间接提交给线程不放弃它们;
  • PriorityBlockingQueue:一个反对优先级排序的无界阻塞队列;
  • DelayQueue:一个应用优先级队列实现的无界阻塞队列,只有在提早期满时能力从中提取元素;
  • LinkedTransferQueue:一个由链表构造组成的无界阻塞队列。与 SynchronousQueue 相似,还含有非阻塞办法;
  • LinkedBlockingDeque:一个由链表构造组成的双向阻塞队列。

较罕用的是 LinkedBlockingQueue 和 Synchronous,线程池的排队策略与 BlockingQueue 无关。

参数 6:threadFactory

线程工厂,次要用来创立线程,默认为失常优先级、非守护线程。

参数 7:handler

回绝策略,回绝解决工作时的策略,零碎提供了 4 种可选:

  • AbortPolicy:回绝并抛出异样。
  • CallerRunsPolicy:应用以后调用的线程来执行此工作。
  • DiscardOldestPolicy:摈弃队列头部(最旧)的一个工作,并执行当前任务。
  • DiscardPolicy:疏忽并摈弃当前任务。

默认策略为 AbortPolicy。

3. 锁降级的过程?

锁降级的过程指的是 synchronized 锁降级的过程,synchronized 锁降级机制也叫做锁收缩机制,此机制诞生于 JDK 6 中。

在 Java 6 及之前的版本中,synchronized 的实现次要依赖于操作系统的 mutex 锁(重量级锁),而在 Java 6 及之后的版本中,Java 对 synchronized 进行了降级,引入了锁降级的机制,能够更加高效地利用 CPU 的多级缓存,晋升了多线程并发性能。

synchronized 锁降级的过程能够分为以下四个阶段:无锁状态、偏差锁、轻量级锁和重量级锁。其中,无锁状态和偏差锁状态都属于乐观锁,不须要进行锁降级,锁竞争较少,可能进步程序的性能。只有在锁竞争强烈的状况下,才会进行锁降级,将锁降级为轻量级锁状态。

上面是 synchronized 锁降级的具体流程:

1. 无锁状态
当一个线程拜访一个同步块时,如果该同步块没有被其余线程占用,那么该线程就能够间接进入同步块,并且将同步块标记为偏差锁状态。这个过程不须要进行任何加锁操作,属于乐观锁状态。

2. 偏差锁状态
在偏差锁状态下,同步块曾经被一个线程占用,其余线程拜访该同步块时,只须要判断该同步块是否被以后线程占用,如果是,则间接进入同步块。这个过程不须要进行任何加锁操作,依然属于乐观锁状态。

3. 轻量级锁状态
如果在偏差锁状态下,有多个线程竞争同一个同步块,那么该同步块就会降级为轻量级锁状态。此时,每个线程都会在本人的 CPU 缓存中保留该同步块的正本,并通过 CAS(Compare and Swap)操作来对同步块进行加锁和解锁。这个过程须要进行加锁操作,但绝对于传统的 mutex 锁,轻量级锁的效率要高很多。

4. 重量级锁状态
轻量级锁之后会通过自旋来获取锁,自旋执行肯定次数之后还未胜利获取到锁,此时就会降级为重量级锁,并且进入阻塞状态。

synchronized 锁降级的过程能够无效地缩小锁竞争,进步多线程并发性。

4.i++ 如何保障线程平安?

保障 i++ 线程平安的伎俩是加锁,能够通过 synchronized 或 Lock 加锁来保障 i++ 的线程平安。

5.HashMap 和 ConcurrentHashMap 有什么区别?

HashMap 和 ConcurrentHashMap 是 Map 接口的具体实现,ConcurrentHashMap 能够看作是 HashMap 的线程平安版本,它们的具体区别如下:

  1. 线程安全性

    • HashMap:HashMap 是非线程平安的。如果多个线程同时拜访和批改 HashMap,没有适当的同步机制的话,可能会导致不统一的后果或者抛出 ConcurrentModificationException 异样。
    • ConcurrentHashMap:ConcurrentHashMap 是线程平安的。多个线程能够同时读取和批改 ConcurrentHashMap,而不会导致数据不统一或者抛出异样。它应用了一种称为 ” 分段锁 ”(Segmented Locking)的技术,将整个数据结构分成多个局部,每个局部都有一个独立的锁。这样,在多线程环境下,不同的线程能够同时操作不同的局部,从而进步并发性能。
  2. 性能

    • HashMap:HashMap 在单线程环境下通常具备更好的性能,因为它不须要额定的同步开销。
    • ConcurrentHashMap:ConcurrentHashMap 在高并发环境下具备更好的性能,因为它应用了分段锁技术,多个线程能够同时操作不同的局部,从而缩小了竞争和阻塞。
  3. Null 值和 Null 键

    • HashMap:HashMap 容许应用 null 作为值和键。
    • ConcurrentHashMap:ConcurrentHashMap 不容许应用 null 作为键,但容许应用 null 作为值。

      6.@Autowired 和 @Resource 区别?

      @Autowired 和 @Resource 都是 Spring/Spring Boot 我的项目中,用来进行依赖注入的注解。它们都提供了将依赖对象注入到以后对象的性能,但二者却有以下不同:

  4. 起源不同:@Autowired 和 @Resource 来自不同的“父类”,其中 @Autowired 是 Spring 定义的注解,而 @Resource 是 Java 定义的注解,它来自于 JSR-250(Java 250 标准提案);
  5. 依赖查找的程序不同:@Autowired 是先依据类型(byType)查找,如果存在多个 Bean 再依据名称(byName)进行查找;而 @Resource 是先依据名称查找,如果(依据名称)查找不到,再依据类型进行查找;
  6. 反对的参数不同:@Autowired 只反对设置一个 required 的参数,而 @Resource 反对更多的参数设置,@Resource 反对 7 个参数的设置;
  7. 依赖注入的反对不同:@Autowired 反对属性注入、构造方法注入和 Setter 注入,而 @Resource 只反对属性注入和 Setter 注入;
  8. 编译器 IDEA 的提醒不同:当应用 IDEA 专业版注入 Mapper 对象时,应用 @Autowired 编译器会提醒报错信息(尽管报错但不印象程序的执行);而 @Resource 则不会报错。

    7. 说说罕用的设计模式

    说到设计模式能够举一些常见的设计模式,以及这些设计模式的具体利用,比方以下这些:

  9. 工厂模式(Factory Pattern):工厂模式是一种创立型设计模式,它提供了一种创建对象的形式,使得应用程序能够更加灵便和可保护。比方在 Spring 中,FactoryBean 就是一个工厂模式的实现,应用它的工厂模式就能够创立进去其余的 Bean 对象;
  10. 单例模式(Singleton Pattern):单例模式是一种创立型设计模式,它保障一个类只有一个实例,并提供了一个全局拜访点。比方在 Spring 中,所以的 Bean 默认是单例的,这意味着每个 Bean 只会被创立一次,并且能够在整个应用程序中共享;
  11. 代理模式模式(Proxy Pattern):代理模式是一种结构型设计模式,它容许开发人员在不批改原有代码的状况下,向应用程序中增加新的性能。比方在 Spring AOP(面向切面编程)就是应用代理模式的实现,它容许开发人员在办法调用前后执行一些自定义的操作,比方日志记录、性能监控等;
  12. 模板办法模式(Template Pattern):模板办法模式是最罕用的设计模式之一,它是指定义一个操作算法的骨架,而将一些步骤的实现提早到子类中去实现,使得子类能够不扭转一个算法的构造即可重定义该算法的某些特定步骤。此模式是基于继承的思维实现代码复用的。比方在 MyBatis 中的典型代表 BaseExecutor,在 MyBatis 中 BaseExecutor 实现了大部分 SQL 执行的逻辑;
  13. 观察者模式(Observer Pattern):定义了一种一对多的依赖关系,当一个对象的状态产生扭转时,所有依赖于它的对象都会失去告诉并自动更新。比方事件驱动、消息传递等性能时,能够应用观察者模式,例如 Spring Event 事件机制;
  14. 适配器模式(Adapter Pattern):适配器模式是一种结构型设计模式,它容许开发人员将一个类的接口转换成另一个类的接口,以满足客户端的需要。在 Spring 中,适配器模式罕用于将不同类型的对象转换成对立的接口,比方将 Servlet API 转换成 Spring MVC 的控制器接口。

8.Redis 为什么这么快?

Redis 运行比拟快的起因有以下几个:

  1. 内存存储:Redis 次要是将数据存储在内存中,而不是磁盘上。相比于传统的磁盘存储数据库系统,内存访问速度更快,因而能够实现更低的提早和更高的吞吐量;
  2. 单线程模型:Redis 采纳单线程模型来解决客户端的申请。这意味着不会产生多线程之间的锁竞争和上下文切换,防止了因为线程切换而导致的性能损耗。此外,单线程模型使得 Redis 的代码更加简略和可预测;
  3. 非阻塞 I /O:Redis 应用非阻塞 I/O 模型来解决网络申请。它通过应用事件驱动的形式解决并发连贯,充分利用了操作系统提供的异步 I/O 性能。这使得 Redis 可能高效地解决大量并发申请,而不会被阻塞;
  4. 高效的数据结构:Redis 提供了多种高效的数据结构,如字符串、哈希表、列表、汇合和有序汇合等。这些数据结构在内存中间接存储和操作数据,使得 Redis 可能以常数工夫复杂度 (O(1))来执行许多常见的操作,如插入、删除和查找;
  5. 异步操作:Redis 反对异步操作,能够将一些耗时的操作(如长久化)放到后盾进行,不会阻塞其余的操作;
  6. 轻量级:Redis 自身是一个十分轻量级的软件,它应用 C 语言编写,代码简洁高效。它没有简单的依赖和额定的形象层,因而能够更快地启动和运行。

9. 索引的品种?如何优化?

MySQL 索引依据不同的维度能够分为不同类型,比方以下这些:

  1. 依据数据结构分类可分为:B+ tree 索引、Hash 索引、Full-Text 索引
  2. 依据物理存储分类可分为:聚簇索引、二级索引(辅助索引、非聚簇索引)
  3. 依据字段个性分类可分为:主键索引、一般索引、惟一索引、前缀索引
  4. 依据字段个数分类可分为:单列索引、联结索引(复合索引、组合索引)

    索引优化

    索引优化能够从以下几个方面动手:

  5. 抉择适当的索引类型:MySQL 提供了不同类型的索引,包含 B-tree、哈希、全文等。依据查问的特点和数据的个性,抉择适合的索引类型。B-tree 索引是最罕用的索引类型,实用于范畴查问和排序操作;
  6. 抉择适合的索引列:抉择对查问频率高且选择性好的列作为索引列。选择性是指索引列中不反复值的比例,选择性越高,索引的成果越好。防止在索引中蕴含过多反复值或过长的列;
  7. 尽量应用聚簇索引:聚簇索引的叶子节点存储了具体的数据,不必在像非聚簇索引一样进行回表查问,所以在查问时,尽量抉择聚簇索引;
  8. 防止过多的索引:索引会占用存储空间,并且在数据更新时须要保护索引,过多的索引会减少保护的开销。只创立必要的索引,防止创立过多的索引;
  9. 应用索引提醒:在某些状况下,MySQL 的查问优化器可能抉择了不现实的查问打算。能够应用索引提醒(Index Hint)来领导优化器抉择正确的索引;
  10. 定期监控和优化:继续监控数据库的性能指标,如查问执行工夫、索引应用状况等。依据监控后果,对索引进行调整和优化,以放弃数据库的高性能。

10. 算法题:合并重叠区间

解题思路和实现代码参考:https://leetcode.cn/problems/merge-intervals/solution/he-bing…

小结

从下面的题能够看进去,团子的整体面试题是不难的,能够说社招的面试难度,当初是小于校招的面试难度的,这也可能和校招宏大的竞争者群体无关。所以如果是社招的哥们,也能够定期骑驴找马试试水,一是测验本人能力是否曾经落后与用人市场;二是,万一有惊喜,拿到更好的 offer,也就开启了职场的新篇章。


本文已收录到我的面试小站 www.javacn.site,其中蕴含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、音讯队列等模块。

正文完
 0