面试官:第一轮提问开始,首先问你,Java 中的多线程有哪些实现方式?
王铁牛:嗯……有继承 Thread 类和实现 Runnable 接口这两种方式。
面试官:回答正确,还不错。那线程池的核心参数有哪些?
王铁牛:这个我知道,有 corePoolSize、maximumPoolSize、keepAliveTime、unit 和 workQueue。
面试官:很好。再问一个,简述一下 HashMap 的工作原理。
王铁牛:呃……就是通过 key 的哈希值找到对应的桶位置,然后在桶里存数据或者取数据。
面试官:好,第一轮提问结束。
面试官:现在进入第二轮提问。说说 JVM 的内存结构。
王铁牛:有堆、栈、方法区、程序计数器、本地方法栈。
面试官:那 Spring 框架的核心特性有哪些?
王铁牛:嗯……能让对象之间解耦,还有依赖注入啥的。
面试官:Spring Boot 的优点是什么?
王铁牛:好像……能快速搭建项目,自动配置啥的。
面试官:第二轮提问完毕。
面试官:最后一轮提问。MyBatis 的缓存机制是怎样的?
王铁牛:呃……有一级缓存和二级缓存。
面试官:Dubbo 的服务注册与发现原理是什么?
王铁牛:不太清楚……
面试官:RabbitMq 的工作模式有哪些?
王铁牛:乱说一通。
面试官:好了,面试结束。回家等通知吧。
答案:
- Java 多线程实现方式:
- 继承 Thread 类:定义一个类继承 Thread 类,重写 run 方法,在 run 方法中编写线程执行的逻辑。通过创建该类的实例对象并调用 start 方法来启动线程。例如:
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行的逻辑");
}
}
MyThread thread = new MyThread();
thread.start();
- 实现 Runnable 接口:定义一个类实现 Runnable 接口,实现其中的 run 方法。通过创建 Thread 类的实例对象,并将实现 Runnable 接口的类的实例作为参数传递给 Thread 类的构造函数,然后调用 start 方法启动线程。例如:
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程执行的逻辑");
}
}
Thread thread = new Thread(new MyRunnable());
thread.start();
- 线程池核心参数:
- corePoolSize:线程池的核心线程数,当提交的任务数小于 corePoolSize 时,线程池会创建新线程来执行任务。
- maximumPoolSize:线程池允许的最大线程数,当提交的任务数大于 corePoolSize 且任务队列已满时,会创建新线程,直到线程数达到 maximumPoolSize。
- keepAliveTime:线程池中非核心线程的存活时间,当线程数大于 corePoolSize 且线程空闲时间超过 keepAliveTime 时,非核心线程会被销毁。
- unit:keepAliveTime 的时间单位。
- workQueue:任务队列,用于存放提交的任务,当线程池线程忙碌时,任务会被放入该队列中。
- HashMap 工作原理:
- 首先计算 key 的哈希值,通过哈希值找到对应的桶位置。
- 在桶中,以链表或红黑树的形式存储键值对。当哈希值冲突时,会将新的键值对添加到链表或红黑树中。
- 当进行 get 操作时,同样通过 key 的哈希值找到桶位置,然后在桶中遍历链表或红黑树找到对应的 value。
- JVM 内存结构:
- 堆:是 JVM 中最大的内存区域,用于存储对象实例,是垃圾回收的主要区域。
- 栈:每个线程都有自己的栈,用于存储局部变量、方法调用等。
- 方法区:存储类信息、常量、静态变量等。
- 程序计数器:记录当前线程执行的字节码指令地址。
- 本地方法栈:用于执行本地方法。
- Spring 框架核心特性:
- 依赖注入(Dependency Injection):通过控制反转(IoC),将对象的依赖关系由程序主动创建改为由容器注入,实现对象之间的解耦。
- 面向切面编程(AOP):可以在不修改原有代码的基础上,为程序动态添加功能,如日志记录、事务管理等。
- IoC 容器:负责创建、配置和管理对象,使得对象之间的依赖关系更加清晰和易于维护。
- Spring Boot 优点:
- 快速搭建项目:提供了一系列的 Starter 依赖,能够快速构建项目框架,减少了繁琐的配置。
- 自动配置:根据项目引入的依赖,自动配置相关的组件和功能,极大地简化了开发过程。
- 内置服务器:内置了 Tomcat 等服务器,方便开发和部署,无需额外配置服务器。
- 监控与管理:提供了 Actuator 等功能,方便对应用进行监控和管理。
- MyBatis 缓存机制:
- 一级缓存:是 SqlSession 级别的缓存,在同一个 SqlSession 中,对相同 SQL 的查询会从缓存中获取数据,不会再次查询数据库。当 SqlSession 关闭时,一级缓存会被清空。
- 二级缓存:是基于 namespace 级别的缓存,多个 SqlSession 可以共享二级缓存。开启二级缓存后,数据会先存到一级缓存,当 SqlSession 提交或关闭时,会将一级缓存的数据同步到二级缓存。下次查询时,如果一级缓存没有命中,会从二级缓存中查询。
- Dubbo 服务注册与发现原理:
- 服务注册:服务提供者启动时,会将自己提供的服务信息(如接口、实现类、服务地址等)通过 Dubbo 协议封装成 RegisterRequest 请求,发送给注册中心(如 Zookeeper)。注册中心接收到请求后,会将服务信息存储起来。
- 服务发现:服务消费者启动时,会向注册中心发送 SubscribeRequest 请求,订阅自己需要的服务。注册中心会将符合条件的服务信息返回给服务消费者。服务消费者根据返回的服务信息,通过 Dubbo 协议与服务提供者建立连接,进行远程调用。
- RabbitMq 工作模式:
- 简单模式(Simple):一个生产者,一个消费者,生产者将消息发送到队列,消费者从队列中获取消息。
- 工作队列模式(Work Queues):多个消费者监听同一个队列,消息会在消费者之间平均分配。
- 发布/订阅模式(Publish/Subscribe):生产者将消息发送到交换机,交换机根据绑定规则将消息发送到多个队列,多个消费者监听不同的队列获取消息。
- 路由模式(Routing):生产者将消息发送到交换机,交换机根据路由键将消息发送到绑定了对应路由键的队列,消费者从队列获取消息。
- 主题模式(Topics):生产者将消息发送到交换机,交换机根据消息的路由键和绑定键的匹配规则将消息发送到绑定了相应主题的队列,消费者从队列获取消息。