面试官:第一轮面试开始。首先,说说 Java 中的多线程创建方式有哪些?
王铁牛:可以通过继承 Thread 类,重写 run 方法来创建;还能实现 Runnable 接口,重写 run 方法;另外还有实现 Callable 接口,通过 FutureTask 来创建线程。
面试官:不错,回答得挺准确。那线程池的核心参数有哪些?
王铁牛:有 corePoolSize(核心线程数)、maximumPoolSize(最大线程数)、keepAliveTime(线程存活时间)、unit(时间单位)、workQueue(任务队列)、threadFactory(线程工厂)、handler(拒绝策略)。
面试官:很好,对基础知识掌握得不错。再问一个,HashMap 在 JDK1.7 和 JDK1.8 中有哪些主要区别?
王铁牛:在 JDK1.7 中是数组 + 链表结构,采用头插法插入元素;JDK1.8 中是数组 + 链表 + 红黑树结构,采用尾插法插入元素,当链表长度大于 8 且数组长度大于 64 时会转为红黑树。
面试官:第二轮面试。说说 Spring 框架中 IoC 和 AOP 的概念。
王铁牛:IoC 就是控制反转,把对象的创建和依赖注入交给 Spring 容器来管理;AOP 是面向切面编程,通过动态代理在不修改原有代码的基础上增强功能。
面试官:那 Spring Boot 自动配置的原理是什么?
王铁牛:Spring Boot 会根据类路径下的依赖自动配置相关的 bean,通过 @EnableAutoConfiguration 注解开启自动配置,底层是利用 SpringFactoriesLoader 加载 META-INF/spring.factories 文件中的自动配置类。
面试官:Spring Boot 中如何实现自定义配置?
王铁牛:可以创建一个配置类,用 @Configuration 注解标注,然后在类中定义各种配置方法,也可以通过 @PropertySource 注解加载自定义的属性文件。
面试官:第三轮面试。Dubbo 中的服务注册与发现是怎么实现的?
王铁牛:Dubbo 是通过 Zookeeper 来实现服务注册与发现的,服务提供者将服务信息注册到 Zookeeper 中,服务消费者从 Zookeeper 中获取服务提供者的信息。
面试官:RabbitMq 中如何保证消息的可靠性?
王铁牛:可以通过设置交换机的持久化、队列的持久化、消息的持久化,还有确认机制来保证消息的可靠性。
面试官:xxl-job 中执行器是如何与调度中心通信的?
王铁牛:不太清楚,瞎猜一下,可能是通过网络请求吧。
面试官:面试就到这里,回去等通知吧。
答案:
- Java 多线程创建方式:
- 继承 Thread 类:创建一个类继承 Thread 类,重写 run 方法,然后创建该类的实例调用 start 方法启动线程。这种方式的缺点是 Java 是单继承,继承了 Thread 类后就不能再继承其他类了。
- 实现 Runnable 接口:创建一个类实现 Runnable 接口,重写 run 方法,然后将该类的实例作为参数传递给 Thread 类的构造函数来创建线程。这种方式避免了单继承的局限性,一个类可以同时实现多个接口。
- 实现 Callable 接口:创建一个类实现 Callable 接口,重写 call 方法,该方法有返回值。通过 FutureTask 类来包装 Callable 对象,然后将 FutureTask 对象作为参数传递给 Thread 类的构造函数来创建线程。通过 FutureTask 的 get 方法可以获取 call 方法的返回值。
- 线程池核心参数:
- corePoolSize:核心线程数,当提交的任务数小于 corePoolSize 时,线程池会创建新的线程来执行任务。
- maximumPoolSize:最大线程数,当提交的任务数大于 corePoolSize 且任务队列已满时,会创建新的线程来执行任务,直到线程数达到 maximumPoolSize。
- keepAliveTime:线程存活时间,当线程数大于 corePoolSize 时,多余的线程在空闲时间超过 keepAliveTime 时会被销毁。
- unit:时间单位,指定 keepAliveTime 的时间单位。
- workQueue:任务队列,用于存放提交的任务,当线程数达到 corePoolSize 时,新提交的任务会放入任务队列中。
- threadFactory:线程工厂,用于创建线程,可自定义线程的名称、优先级等属性。
- handler:拒绝策略,当线程数达到 maximumPoolSize 且任务队列已满时,会调用拒绝策略来处理新提交的任务。常见的拒绝策略有 AbortPolicy(抛出异常)、CallerRunsPolicy(调用者运行)、DiscardPolicy(丢弃)、DiscardOldestPolicy(丢弃最旧的任务)。
- HashMap 在 JDK1.7 和 JDK1.8 中的区别:
- 数据结构:JDK1.7 是数组 + 链表结构,JDK1.8 是数组 + 链表 + 红黑树结构。
- 插入元素方式:JDK1.7 采用头插法插入元素,JDK1.8 采用尾插法插入元素。头插法在多线程环境下可能会导致链表形成环形结构,从而引发死循环问题,而尾插法避免了这个问题。
- 链表转红黑树:JDK1.8 中当链表长度大于 8 且数组长度大于 64 时会将链表转为红黑树,目的是为了提高查找效率,因为红黑树的查找时间复杂度为 O(logn),而链表的查找时间复杂度为 O(n)。
- Spring 框架中 IoC 和 AOP 的概念:
- IoC(控制反转):传统的应用程序中,对象之间的依赖关系是由开发者在代码中直接创建和管理的。而在 IoC 容器中,对象的创建和依赖注入由容器来管理。开发者只需要定义好对象之间的依赖关系,容器会自动创建对象并注入依赖。这样可以降低对象之间的耦合度,提高代码的可维护性和可测试性。
- AOP(面向切面编程):AOP 是一种编程范式,它允许开发者将横切关注点(如日志记录、事务管理、权限验证等)与业务逻辑分离。通过动态代理技术,在不修改原有业务代码的基础上,为目标对象添加额外的功能。AOP 主要通过切点、通知和切面来实现,切点定义了需要应用通知的连接点,通知定义了在切点处执行的具体操作,切面是切点和通知的组合。
- Spring Boot 自动配置的原理: Spring Boot 自动配置的核心是 @EnableAutoConfiguration 注解,它会导入 AutoConfigurationImportSelector 类。该类通过 SpringFactoriesLoader 从 META-INF/spring.factories 文件中加载所有的自动配置类。这些自动配置类会根据类路径下的依赖情况,自动配置相关的 bean。例如,如果类路径下有数据库相关的依赖,那么会自动配置数据源、事务管理器等 bean。Spring Boot 通过这种方式,极大地简化了项目的配置过程,开发者只需要引入相关的依赖,Spring Boot 就能自动完成大部分的配置工作。
- Spring Boot 中实现自定义配置:
- 创建配置类:使用 @Configuration 注解标注一个类,该类就是一个配置类。在配置类中可以使用 @Bean 注解定义各种 bean,这些 bean 会被 Spring Boot 容器管理。
- 加载自定义属性文件:可以使用 @PropertySource 注解加载自定义的属性文件,例如 @PropertySource("classpath:config.properties"),然后在配置类中通过 @Value 注解注入属性值,如 @Value("${username}") String username。也可以创建一个配置类实现 EnvironmentAware 接口,通过 Environment 对象获取属性值。
- Dubbo 中的服务注册与发现实现: Dubbo 基于 Zookeeper 实现服务注册与发现。服务提供者启动时,会将自身的服务信息(包括服务接口、实现类、服务地址等)注册到 Zookeeper 的指定节点下。服务消费者启动时,会从 Zookeeper 中订阅服务提供者的信息。当服务提供者的服务信息发生变化(如服务下线、上线、地址变更等),Zookeeper 会通知服务消费者。服务消费者根据获取到的服务提供者信息,通过网络调用服务提供者的服务。Dubbo 通过这种方式实现了服务的动态注册与发现,提高了系统的可扩展性和灵活性。
- RabbitMq 中保证消息可靠性的方法:
- 交换机持久化:通过设置交换机的 durable 属性为 true,确保交换机在 RabbitMq 重启后不会丢失。
- 队列持久化:设置队列的 durable 属性为 true,保证队列在 RabbitMq 重启后依然存在。
- 消息持久化:发送消息时设置消息的 deliveryMode 属性为 2,表示消息持久化。这样即使 RabbitMq 重启,消息也不会丢失。
- 确认机制:
- 生产者确认:生产者发送消息后,可以通过 confirm 机制确认消息是否成功到达交换机。如果设置了 mandatory 参数为 true,当消息无法路由到队列时,会返回给生产者。
- 消费者确认:消费者可以通过手动确认或自动确认的方式确认消息的消费。手动确认可以保证消息在处理成功后才被确认,避免消息丢失。自动确认是在消息被接收后立即确认,可能会导致消息在处理过程中丢失。
以上就是这些问题的详细答案,希望对小白学习有所帮助。