面试官:第一轮提问开始。首先,说一下Java中的多线程有哪些实现方式?
王铁牛:嗯,有继承Thread类,还有实现Runnable接口。
面试官:不错,回答正确。那再问个问题,线程池的核心参数有哪些?
王铁牛:这个嘛,好像有corePoolSize、maximumPoolSize、keepAliveTime这些。
面试官:还行。最后一个问题,简述一下HashMap的工作原理。
王铁牛:它就是通过key的哈希值来确定存储位置的。
面试官:好,第一轮表现不错。接下来第二轮提问。说说Spring框架中IoC和AOP的概念。
王铁牛:IoC就是控制反转,AOP是面向切面编程。
面试官:还算准确。那Spring Boot的自动配置原理是什么?
王铁牛:呃,这个不太清楚,大概就是能自动配置一些东西吧。
面试官:最后一个问题,MyBatis的缓存机制是怎样的?
王铁牛:好像有一级缓存和二级缓存。
面试官:好,第二轮也结束了。现在进入第三轮提问。Dubbo的集群容错策略有哪些?
王铁牛:这个,有啥来着,忘了。
面试官:RabbitMq的消息确认机制了解吗?
王铁牛:不太明白。
面试官:xxl-job的调度中心原理是什么?
王铁牛:完全不知道。
面试官:好了,三轮提问结束。整体来说,简单问题回答得还可以,但复杂问题的回答不太理想。回去等通知吧。
答案:
- Java多线程实现方式:
- 继承Thread类:通过继承Thread类并重写其run方法来定义线程执行的任务。例如:
然后可以通过创建MyThread类的实例并调用start方法来启动线程:class MyThread extends Thread { @Override public void run() { System.out.println("线程执行的任务"); } }MyThread thread = new MyThread(); thread.start();- 实现Runnable接口:实现Runnable接口的类需要实现其run方法。例如:
然后通过创建Thread类的实例并传入MyRunnable实例来启动线程: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:线程池中非核心线程的存活时间。当线程空闲时间超过keepAliveTime时,非核心线程会被销毁。
- unit:keepAliveTime的时间单位。
- workQueue:线程池的任务队列,用于存放提交的任务。
- threadFactory:线程池创建线程的工厂。
- handler:当线程池无法处理新任务时的拒绝策略。
- HashMap工作原理:
- HashMap基于哈希表实现。它通过计算key的哈希值来确定元素在数组中的存储位置。
- 当向HashMap中插入元素时,首先计算key的哈希值,然后通过哈希值与数组长度进行位运算得到数组下标。
- 如果该下标位置为空,则直接插入新元素。
- 如果该下标位置不为空,则会发生哈希冲突。此时会通过链表或红黑树来解决冲突。
- 在获取元素时,同样先计算key的哈希值,找到对应的数组下标,然后在链表或红黑树中查找是否存在该key,若存在则返回对应的值。
- Spring框架中IoC和AOP的概念:
- IoC(控制反转):
- 传统的软件开发中,对象之间的依赖关系由程序自身主动创建和管理。
- 在IoC容器中,对象的创建和依赖关系的管理由容器负责。对象只需要声明自己的依赖,容器会自动注入这些依赖。
- 例如,一个Service类依赖于一个Dao类,在IoC容器中,容器会自动创建Dao类的实例并注入到Service类中。这样,对象之间的依赖关系由容器控制,而不是对象自身,实现了控制反转。
- AOP(面向切面编程):
- AOP是一种编程范式,旨在将横切关注点(如日志记录、事务管理、安全检查等)与业务逻辑分离。
- 它通过动态代理等技术,在不修改原有业务逻辑代码的基础上,为程序添加额外的功能。
- 比如,有多个业务方法都需要进行日志记录,使用AOP可以将日志记录的代码封装成一个切面,然后在需要的地方进行切入,而不需要在每个业务方法中都重复编写日志记录代码。
- IoC(控制反转):
- Spring Boot自动配置原理:
- Spring Boot的自动配置基于条件化配置。它通过@Conditional注解等机制来判断当前环境是否满足配置的条件。
- 当应用启动时,Spring Boot会扫描classpath下的所有META-INF/spring.factories文件。
- 这些文件中定义了各种自动配置类,例如DataSourceAutoConfiguration(数据源自动配置)等。
- 自动配置类会根据当前应用的环境信息(如是否存在特定的依赖、配置文件中的属性等)来决定是否生效。
- 如果条件满足,自动配置类会向Spring容器中注册相应的Bean,配置各种组件和属性,从而实现应用的自动配置。
- MyBatis缓存机制:
- 一级缓存:
- 一级缓存是SqlSession级别的缓存。当执行同一个SqlSession中的查询时,如果查询条件相同,会直接从缓存中获取数据,而不会再次查询数据库。
- 例如,在同一个SqlSession中,先执行了一次查询
select * from user where id = 1,之后再次执行相同的查询,就会从一级缓存中获取结果,而不是重新执行SQL。 - 当SqlSession关闭时,一级缓存会被清空。
- 二级缓存:
- 二级缓存是Mapper级别的缓存。多个SqlSession可以共享二级缓存。
- 开启二级缓存后,在一个Mapper中执行的查询结果会被缓存起来。不同的SqlSession在执行相同的Mapper查询时,如果二级缓存中有数据且查询条件相同,就可以直接从缓存中获取。
- 二级缓存的实现需要在Mapper.xml文件中配置
<cache>标签等相关内容,并且需要将实体类实现Serializable接口。
- 一级缓存:
- Dubbo集群容错策略:
- Failover Cluster:失败自动切换,当出现调用失败时,会自动切换到其他服务器进行重试。
- Failfast Cluster:快速失败,只要有一个调用失败,就立即返回错误,不再进行重试。
- Failsafe Cluster:失败安全,调用失败时,直接忽略,不抛出异常。
- Failback Cluster:失败自动恢复,调用失败时,会在后台记录失败请求,然后定时重试。
- Forking Cluster:并行调用多个服务器,只要有一个成功就返回。
- RabbitMq消息确认机制:
- 生产者确认机制:
- 事务机制:生产者发送消息前开启事务(channel.txSelect()),发送消息后根据事务执行结果进行提交(channel.txCommit())或回滚(channel.txRollback())。如果消息发送成功但事务回滚,消息会被退回。
- Confirm机制:生产者设置ConfirmCallback,当消息发送到Broker后,Broker会返回确认结果(ack),生产者根据确认结果进行后续处理。
- 消费者确认机制:
- 自动确认:消费者接收到消息后,RabbitMQ会自动将该消息标记为已确认,即使消费者还没有处理完消息。
- 手动确认:消费者接收到消息后,需要通过调用channel.basicAck方法手动确认消息已被处理。可以选择确认单条消息或批量确认。
- 生产者确认机制:
- xxl-job调度中心原理:
- 调度中心核心组件:
- 调度器:负责按照配置的调度策略触发任务执行。它根据任务的执行时间、执行周期等信息,计算出任务的下次执行时间,并触发任务调度。
- 执行器管理:管理注册到调度中心的执行器。执行器是实际执行任务的节点,调度中心会与执行器进行通信,下发任务并获取执行结果。
- 任务管理:存储和管理任务的配置信息,包括任务的执行逻辑、执行时间、执行周期等。
- 调度流程:
- 调度中心启动后,会加载配置的任务信息。
- 当任务的执行时间到达时,调度器会根据任务配置找到对应的执行器,并将任务信息发送给执行器。
- 执行器接收到任务后,按照任务的执行逻辑进行处理,并将执行结果反馈给调度中心。
- 调度中心根据执行结果进行后续处理,如记录任务执行状态、触发下一次调度等。
- 调度中心核心组件: