《互联网大厂Java面试:核心知识大考验》

43 阅读5分钟

面试官:请简要介绍一下 Java 中的多线程机制,比如线程的创建方式有哪些?

王铁牛:线程创建方式有继承 Thread 类、实现 Runnable 接口、实现 Callable 接口。

面试官:嗯,回答得不错。那说说线程池的作用和优势吧。

王铁牛:线程池能复用线程,减少线程创建和销毁开销,提高响应速度,还能控制并发数量。

面试官:很好。接下来问几个关于 JVM 的问题,类加载机制分哪几个阶段?

王铁牛:类加载机制分加载、验证、准备、解析、初始化阶段。

面试官:第一轮面试结束,整体表现还行。下面进入第二轮。说说 HashMap 的底层数据结构以及扩容机制。

王铁牛:HashMap 底层是数组+链表+红黑树。扩容就是当元素个数超过阈值且链表长度大于 8 时,链表会转换为红黑树,当元素个数小于 6 时,红黑树又会转换为链表,然后数组扩容。

面试官:ArrayList 的优缺点是什么?

王铁牛:优点是随机访问速度快,缺点是插入和删除效率低,因为涉及元素移动。

面试官:Spring 框架中依赖注入的方式有哪些?

王铁牛:有构造器注入、setter 方法注入、基于注解的注入。

面试官:第二轮结束,表现一般。现在进入第三轮。Dubbo 的集群容错模式有哪些?

王铁牛:有 failover、failfast、failsafe、failback、forking 等。

面试官:RabbitMq 的消息确认机制是怎样的?

王铁牛:有发送确认和接收确认,发送确认又分事务确认和 publisher confirm 模式,接收确认就是消费者接收到消息后给生产者反馈。

面试官:xxl-job 是如何实现分布式任务调度的?

王铁牛:(胡乱回答一通,讲得很不清晰)

面试官:好的,面试就到这里,回去等通知吧。

答案:

  • 线程创建方式
    • 继承 Thread 类:通过继承 Thread 类并重写 run 方法来创建线程。这种方式的优点是简单直接,缺点是 Java 是单继承,继承了 Thread 类后就不能再继承其他类了。
    • 实现 Runnable 接口:实现 Runnable 接口的 run 方法。这种方式避免了单继承的局限性,更适合多个线程共享一个资源的场景。
    • 实现 Callable 接口:实现 Callable 接口的 call 方法,与 Runnable 不同的是 call 方法有返回值,并且可以抛出异常。
  • 线程池的作用和优势
    • 作用:复用线程,减少线程创建和销毁的开销,提高系统性能和响应速度。
    • 优势
      • 提高响应速度:避免了频繁创建和销毁线程的时间开销。
      • 控制资源消耗:可以限制线程的数量,避免资源耗尽。
      • 便于管理:统一管理线程的创建、销毁和复用。
  • JVM 类加载机制阶段
    • 加载:通过类的全限定名获取二进制字节流,将其加载到内存中,并创建一个 java.lang.Class 对象。
    • 验证:验证字节流的正确性,确保其符合 JVM 的规范。
    • 准备:为类的静态变量分配内存,并设置默认初始值。
    • 解析:将符号引用替换为直接引用。
    • 初始化:执行类的静态代码块和静态变量的赋值操作。
  • HashMap 底层数据结构及扩容机制
    • 底层数据结构:由数组、链表和红黑树组成。当链表长度大于 8 且数组容量大于 64 时,链表会转换为红黑树,以提高查询效率。
    • 扩容机制:当元素个数超过阈值(容量*负载因子)时,数组会进行扩容。扩容时,会创建一个新的更大的数组,然后将原数组中的元素重新计算位置后放入新数组中。
  • ArrayList 优缺点
    • 优点
      • 随机访问速度快:基于数组实现,通过下标可以直接定位到元素。
      • 实现简单:代码结构相对简单。
    • 缺点
      • 插入和删除效率低:插入和删除元素时需要移动大量元素。
      • 占用连续内存空间:可能导致内存碎片。
  • Spring 依赖注入方式
    • 构造器注入:通过构造函数传入依赖对象,优点是注入的依赖对象在构造函数执行时就已经可用,缺点是当依赖对象较多时,构造函数参数列表会很长。
    • setter 方法注入:通过 setter 方法设置依赖对象,优点是可以灵活地更换依赖对象,缺点是可能会出现 NPE(空指针异常),因为在调用 setter 方法前依赖对象可能未被初始化。
    • 基于注解的注入:常用的注解有 @Autowired、@Resource 等,@Autowired 按照类型进行注入,@Resource 可以按照名称或类型进行注入,使用注解可以使代码更加简洁。
  • Dubbo 集群容错模式
    • failover:失败自动切换,当调用失败时,会自动重试其他服务器。
    • failfast:快速失败,当调用失败时,立即抛出异常,不再重试。
    • failsafe:失败安全,当调用失败时,直接忽略,不抛出异常。
    • failback:失败后自动恢复,当调用失败时,会在后台异步重试。
    • forking:并行调用多个服务器,只要有一个成功就返回。
  • RabbitMq 消息确认机制
    • 发送确认
      • 事务确认:通过 channel.txSelect()开启事务,在发送消息后调用 channel.txCommit()提交事务,如果出现异常则调用 channel.txRollback()回滚事务。
      • publisher confirm:通过 channel.confirmSelect()开启确认模式,发送消息后通过 addConfirmListener 监听确认结果。
    • 接收确认:消费者接收到消息后,通过 channel.basicAck 方法向 RabbitMq 发送确认消息,表示已成功接收。
  • xxl-job 实现分布式任务调度
    • 调度中心:负责管理任务调度规则、任务执行器信息等。
    • 执行器:负责实际执行任务。
    • 注册中心:用于调度中心和执行器之间的服务发现和注册。
    • 调度流程:调度中心根据任务调度规则,从注册中心获取可用的执行器,然后将任务分配给执行器执行。执行器执行完任务后,将执行结果反馈给调度中心。