面试官:第一轮提问开始。首先,说说Java中的多线程如何实现线程安全?
王铁牛:嗯……可以用synchronized关键字,还有Lock接口。
面试官:不错,回答正确。那再问一个,线程池的核心参数有哪些,分别有什么作用?
王铁牛:核心参数有corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory和handler。corePoolSize是核心线程数,maximumPoolSize是最大线程数,keepAliveTime是线程存活时间,unit是时间单位,workQueue是任务队列,threadFactory是线程工厂,handler是拒绝策略。
面试官:很好,回答得很全面。接下来,讲讲HashMap的底层数据结构。
王铁牛:它是数组+链表+红黑树。
面试官:嗯,回答正确。第一轮提问结束。
面试官:第二轮提问。Spring框架中的IoC和AOP分别是什么,有什么作用?
王铁牛:IoC是控制反转,把对象的创建和依赖注入交给Spring容器。AOP是面向切面编程,用于处理横切关注点。
面试官:还行。那Spring Boot的自动配置原理是什么?
王铁牛:就是通过@EnableAutoConfiguration注解,自动配置各种组件。
面试官:回答得不太准确。再问,MyBatis的动态SQL有哪些?
王铁牛:有if、where、trim、foreach等。
面试官:第二轮提问结束。
面试官:第三轮提问。Dubbo的集群容错策略有哪些?
王铁牛:有failover、failfast、failsafe、failback、forking等。
面试官:回答得比较混乱。再问,RabbitMq的消息确认机制是怎样的?
王铁牛:不太清楚。
面试官:最后一个问题,xxl-job的核心组件有哪些?
王铁牛:答不上来。
面试官:第三轮提问结束。整体来说,你对一些简单问题回答得还可以,但对于复杂问题的理解和回答不够清晰准确。回去等通知吧。
答案:
- 多线程实现线程安全:
- synchronized关键字:它可以修饰方法或代码块。当一个线程访问被synchronized修饰的方法或代码块时,它会先获取对象的锁。如果锁已经被其他线程持有,那么该线程会进入等待状态,直到锁被释放。例如在一个银行账户类中,对存钱和取钱的方法使用synchronized修饰,就可以保证在多线程环境下账户余额的操作是安全的。
- Lock接口:相比于synchronized,Lock接口提供了更灵活的锁控制。比如ReentrantLock,它可以实现公平锁,即按照线程请求锁的顺序来分配锁,避免某些线程一直等待。还可以通过tryLock方法尝试获取锁,避免死锁情况。
- 线程池核心参数:
- corePoolSize:核心线程数。当提交的任务数小于corePoolSize时,线程池会创建新的线程来执行任务。
- maximumPoolSize:最大线程数。当任务数超过corePoolSize且workQueue已满时,会创建新线程直到线程数达到maximumPoolSize。
- keepAliveTime:线程存活时间。当线程数大于corePoolSize时,多余的线程在空闲了keepAliveTime这么长时间后会被销毁。
- unit:时间单位,用于指定keepAliveTime的时间单位。
- workQueue:任务队列。用于存放提交的任务,当线程都在忙时,新任务会进入这个队列等待。
- threadFactory:线程工厂。用于创建线程,可自定义线程的名称、优先级等属性。
- handler:拒绝策略。当线程数达到maximumPoolSize且workQueue已满时,新任务会被这个策略处理,比如AbortPolicy会直接抛出异常,DiscardPolicy会丢弃新任务,DiscardOldestPolicy会丢弃队列中最老的任务等。
- HashMap底层数据结构:
- 数组:HashMap初始时会创建一个长度为16的数组。
- 链表:当发生哈希冲突时,新的键值对会被添加到链表中。例如两个不同的键通过哈希函数计算出相同的哈希值,就会在数组的同一个位置形成链表。
- 红黑树:当链表长度达到8时,链表会转换为红黑树,以提高查询效率。红黑树是一种自平衡二叉查找树,它能保证在插入、删除等操作后,树的高度相对平衡,从而使得查询时间复杂度保持在O(log n)。
- Spring框架中的IoC和AOP:
- IoC(控制反转):传统的对象创建和依赖管理由应用程序自己负责,而在IoC容器中,对象的创建和依赖注入由容器来完成。比如一个用户服务类需要依赖一个数据库连接类,在IoC容器中,容器会负责创建数据库连接类并注入到用户服务类中。这样做的好处是降低了组件之间的耦合度,方便维护和扩展。
- AOP(面向切面编程):用于处理横切关注点,比如日志记录、事务管理等。这些功能与业务逻辑本身没有直接关系,但又贯穿于多个业务方法中。通过AOP,可以将这些横切关注点与业务逻辑分离,以提高代码的可维护性和复用性。例如在一个电商订单系统中,使用AOP可以在订单创建、支付等业务方法执行前后自动记录日志。
- Spring Boot自动配置原理: Spring Boot的自动配置是基于@EnableAutoConfiguration注解实现的。它会根据类路径下的依赖,自动配置各种组件。比如当项目中引入了Spring Data JPA依赖,Spring Boot会自动配置一个JpaReposityFactoryBean,用于创建JPA的Repository接口实现类。它通过条件注解(如@Conditional)来判断是否需要进行配置,只有满足特定条件时才会创建相应的组件。这样开发者只需要引入相关依赖,Spring Boot就能自动完成很多配置工作,大大简化了开发。
- MyBatis的动态SQL:
- if:根据条件决定是否拼接SQL语句。例如在查询用户时,根据传入的参数决定是否添加查询条件。 AND name = #{name}
- where:会自动处理SQL语句中的WHERE关键字,并且会智能地去掉多余的AND或OR。例如 AND name = #{name} AND age = #{age} ,如果name和age都不为空,会生成正确的WHERE子句,且不会出现多余的AND。
- trim:可以自定义前缀和后缀。比如 AND name = #{name} ,可以指定在拼接的SQL语句前加上WHERE,并且如果前面有多余的AND或OR会去掉。
- foreach:用于循环遍历集合。比如在批量删除用户时, #{id} ,会将ids集合中的每个元素以逗号分隔,放在括号内作为SQL的参数。
- Dubbo集群容错策略:
- failover:失败自动切换。当调用失败时,会自动重试其他服务器,默认重试2次。适用于读操作等对结果准确性要求不是特别高的场景。
- failfast:快速失败。只调用一次,失败立即报错,适用于幂等操作,比如查询操作。
- failsafe:失败安全。调用失败时,直接忽略,不抛出异常,适用于写日志等操作。
- failback:失败自动恢复。失败后会记录下来,然后定时重试,适用于消息通知等异步操作。
- forking:并行调用多个服务器,只要一个成功就返回,用于实时性要求较高的场景,但会消耗更多资源。
- RabbitMq消息确认机制:
- 生产者确认机制:
- 事务机制:生产者发送消息前开启事务(channel.txSelect()),发送消息后如果事务提交成功(channel.txCommit()),则消息发送成功;如果事务回滚(channel.txRollback()),则消息发送失败。但这种方式性能较低。
- Confirm机制:生产者通过调用channel.confirmSelect()开启Confirm模式。发送消息后,通过addConfirmListener监听消息的确认结果。如果消息成功到达Broker,会触发ConfirmListener的handleAck方法;如果消息未成功到达Broker,会触发handleNack方法。
- 消费者确认机制:
- 自动确认:消费者设置autoAck=true,当消息被消费者接收到后,会自动被确认,Broker会将其从队列中删除。
- 手动确认:消费者设置autoAck=false,接收到消息后,需要调用channel.basicAck手动确认消息。可以逐个确认,也可以批量确认。如果在处理消息过程中出现异常,没有手动确认,消息会一直留在队列中,不会被其他消费者消费,直到被手动确认或者达到死信队列的条件。
- 生产者确认机制:
- xxl-job核心组件:
- 调度中心:是xxl-job的核心调度模块,负责管理任务调度规则、触发调度等。它提供了可视化的界面,方便管理员配置任务和查看任务执行情况。
- 执行器:负责实际执行任务。可以是独立的应用程序,也可以是嵌入到现有项目中的模块。执行器会定期向调度中心注册,获取要执行的任务并执行。
- 任务管理:包括任务的创建、编辑、删除等操作。可以定义任务的执行频率、执行参数等。
- 日志管理:记录任务的执行日志,方便排查问题。可以查看任务每次执行的详细日志信息,包括输入参数、执行过程、输出结果等。