1、重写equals为什么还要重写hashcode?
1.1、保证一个原则,两个对象用equals()比较为true,那么这两个对象的hashcode一定相同,如果重写equals(),不重写hashcode,那么会造成两个equals()比较为true的对象hashcode不相同
1.2、在散列集合中,使用hashcode来计算key应该存储在hash表的索引,如果重写equals(),不重写hashcode,会出现两个equals()比较为true的对象hashcode不相同,从而计算出的索引不同,这样集合就乱套了
1.3、为了提高效率,当我们去比较两个对象是否是同一个对象,会先 比较两个对象的hashcode是否相同,如果hashcode不相同,那么就不会在调用equals()来进行比较,减少比对次数提高效率
2、Spring中如果两个bean的id相同,会报错吗 ?
2.1、在同一个xml文件中,bean的id是唯一的,如果出现两个id相同的bean,spring启动的时候就会报错
2.2、在两个不同的xml文件中,可以出现id相同的bean,但是会覆盖
2.3、如果使用@bean注解去声明一个bean,那么bean属性相同的话,也就是声明了多个相同的bean,Spring只会注册第一个声明的bean实例,后面声明的bean不会注册
3、为什么ConcurrentHashMap的key不能为null?
在ConcurrentHashMap的源码中,有个putval()方法,就是当ConcurrentHashMap的key或value为null的时候就会抛出空指针异常,这也是防止在多线程并发的场景下造成一个歧义问题,从而引发线程安全问题。举个例子:当一个线程去获取ConcurrentHashMap的key的值的时候,如果返回null,这时无法判断是这个key的值为null还是这个key不存在,这种不确定性会造成线程安全问题,而ConcurrentHashMap又是一个线程安全的集合,所以才有一个这样的设计
4、springboot 自动装配原理
springboot自动装配是通过spring自动装配来完成的
在spring中,通过使用@Atuowried注解来实现自动装配。当使用@Autowried注解来标注一个属性时,Spring容器会自动创建一个该属性的bean对象,并且将这个对象注入到属性中
除了使用@Auowried注解,还可以使用@Resource注解来实现自动装配。两者区别在于,@Resource注解不仅可以通过类型来自动装配,而且还可以通过名称来实现自动装配
如果Spring容器存在多个符合条件的bean,那么可以使用@Qualifier注解来指定具体使用哪一个bean
在SpringBoot中,自动装配功能还可以通过@EnableAutoConfiguration注解来启用。这个注解可以根据应用所需的依赖自动装配Spring容器
5、@Autowired 和 @Resource 的区别?
5.1、来源不同:
@Autowired 来自 Spring框架,而 @Resource 来自于(Java)JSR-250;
5.2、依赖查找的顺序不同:
@Autowired 先根据类型再根据名称查询,而 @Resource 先根据名称再根据类型查询;
5.3、支持的参数不同:
@Autowired 只支持设置1个参数,而 @Resource 支持设置7 个参数;
5.4、依赖注入的用法支持不同:
@Autowired 既支持构造方法注入,又支持属性注入和 Setter 注入,而@Resource 只支持属性注入和 Setter 注入;
6、如何保持MySQL和Redis的一致性?
例如如果一个事务执行失败回滚了,但是如果采取了先写Redis的方式,就会造成Redis和MySQL数据库的不一致,再比如说,一个事务写入了MySQL,但是此时还未写入Redis,如果这时候有用户访问Redis,则此时就会出现数据不一致。
6.1、同步双写(双写一致性)
6.1.1、先更新MySQL,再更新Redis:
在业务代码中,首先执行对数据库的写操作,写入成功后,立即更新Redis缓存,这种方式简单直接,但也存在短暂的数据不一致风险,就是在MySQL更新后,Redis更新前,如果有请求读取缓存,可能获取的数据不完整
6.1.2、先更新Redis,再更新MySQL:
先更新Redis,然后更新MySQL。这种方式同样存在类似的风险,就是在Redis更新后、MySQL更新前,如果有请求直接查询数据库(如缓存未命中或被主动刷新),可能会看到新缓存数据与旧数据库数据之间的不一致。
6.2、异步更新
使用消息队列:当MySQL数据更新时,将更新事件发送给消息队列(如RabbitMQ,Kafaka等),消费者服务监听队列,接收到消息后异步更新Redis缓存。这种方法可以缓解同步更新时的性能压力,但增加了系统的复杂性,并且需要处理消息丢失、重复消费等问题以保证最终一致性。
6.3、基于MySQL Binlog更新:
利用工具(如Canal、Maxwell、Debezium等)订阅MySQL的二进制日志(Binlog),当MySQL数据发生变化时,这些工具自动捕获并解析Binlog事件,然后将更新信息自动推送到Redis,自动更新缓存。这种方法可以基本做到数据同步,且对业务代码无侵入,但需要额外部署和维护Binlog订阅服务。
6.4、延迟双删策略:
在更新MySQL后,立即删除Redis缓存,然后设置一个短时延(如几毫秒)后再次删除缓存。这样可以尽量减少在第一次删除缓存到MySQL更新生效期间,新数据被缓存的可能性。
6.4.1、线程A先删除缓存,之后更新数据库
6.4.2、线程B和线程C发现缓存没数据,查询数据库。线程B查询到的是旧数据,线程C查询到的是新数据。之后纷纷放入缓存
6.4.3、线程A延时3-5秒(时间一般要大于SQL执行时间+线程切换执行时间100ms足够),再将缓存删除。之后其他线程再查询缓存,发现没数据,再次查询数据库及放入缓存都是新数据
极端情况就是线程D,所以延时双删还是不一定能保证缓存及数据一致。