2022Java面试必问基础总结
2022-04-27 22:39:51 1 举报
AI智能生成
2022Java面试必问基础总结
作者其他创作
大纲/内容
MQ
什么场景?
异步
主流程后异步发送短信之类的
解耦
流量削峰
redis
场景
缓存问题
雪崩
大量key同时间过期或者redis宕机,导致大量请求打入db,导致宕机
随机缓存时间
热点数据永远不过期
缓存主动更新
缓存高可用部署,集群+主备
穿透
大量缓存不存在的key请求,直接打到DB
正常id校验
查不到设置null值
布隆过滤器
击穿
单个key的大流量请求导致的缓存失效击穿DB
加锁
热点数据永不过期
定时更新缓存
db和缓存的不一致问题怎么解决?
结论:终极解决方案:读写锁控制,必须是分布式的读写锁,比如用redis实现,Redission有封装好的功能
目的是严格的控制读写缓存的一致性,可以兼顾大部分流量的读性能,也可以控制写入的一致性
也就是说当进行读操作的时候加读锁,不影响其它的读操作,当发生写操作时加写锁,此时读操作暂时阻塞,直接缓存失效并更新数据库后解锁,那么后续的读操作一定是最新值会更新到缓存。
red lock
简单来讲就是依次从不同的redis节点获取锁,设置锁的过期时间和获取锁的超时时间,过期时间远大于超时时间,当获取锁的redis节点服务宕机,则会快速的超过超时时间获取锁失败,则会立刻尝试获取下一个redis实例的锁,当大部分redis节点都获取到锁,过期时间大于获取锁的总时间则算获取到锁。这样做就是为了避免单个redis服务节点宕机节点切换导致的获取锁失效问题。
原子性
单线程接收,io多路复用放入队列,利用文件分派器分派到不同的命令处理器,扩大了可处理数据的容量,同时保留了原子性
分布式锁
特点
互斥性
锁超时
可重入性
非阻塞
公平非公平
谁加锁谁释放
redisson
使用的redis的hash数据类型,用lua脚本进行的加锁六层
先判断key是否存在,不存在加锁,设置field为客户端id(uuid+thread id),值为1表示加锁次数,设置超时时间30秒
用watch dog监听超时时间,业务没执行完前会每隔十秒进行续期,重置30s
它是可重入的锁,每次加锁value+1
解锁则是每次value-1,直到为0做del操作
Mysql
如何优化
建表时
字段尽量Not null
字段类型长度设计合理
尽量遵循数据库三范式,业务逻辑不耦合
写SQL
like 前置%不可取
尽量不用or not <>
可以覆盖索引尽量用覆盖索引的写法
join不可过多的表,性能会急剧下降,用小表驱动大表
上线后
开启慢SQL日志,定期拉取慢SQL进行优化
explain分析SQL,构建合理的索引
MVCC
聊下mysql的事务隔离级别
重点掌握undo log和readView
readView在RC隔离级别下,每次执行快照都会生成新的读快照
readView在RR隔离级别下,只有在当前事务进行第一次快照读的时候,生成readView,之后快照度都用的当前readView
readView的数据结构
m_ids:当前活跃事务ID集合
min_trx_id
最小活跃事务id,小于它说明事务已经提交
max_trx_id
预分配的事务id,等于当前最大事务id+1
createor_trx_id
readView创建者的事务编号
微服务
如何进行微服务划分?
并发
聊下synchronized
作用:是java中的一个关键词,起到互斥锁的作用
修饰对象
实例方法
锁是对象实例
静态方法
锁是当前类的实例
代码块
锁是传入对象实例
实现原理
修饰方法
用ACC_SYNCHRONIZED来标识
修饰代码块
monitorenter和monitorexit指令
对象实例三部分组成:对象头,对象实际数据、对齐填充,其中对象头的mark word储存着锁信息
jdk1.6之后对synchronized做了很多优化
mark word记录了锁的状态 无锁、偏向锁、轻量级锁、重量级索
锁从无锁到重量级锁的升级过程
volatile
可见性
有序性
happen-before规则有哪些?
程序顺序性规则
一条线程代码顺序执行,前面的代码在后续代码前执行
监视器规则
锁的加锁对解锁可见
volatile规则
volatile的写操作必定对后续的读操作可见
传递性规则
A对B可见,B对C可见,则A对C可见
线程启动规则
线程的start操作对线程执行内容可见
线程终止规则
线程中任意操作比如Thread.join(),Thread.isAlive()对线程的终止监测可见
线程中断规则
interrupt()方法的调用,happen-before与被中断线程的代码检测,也就是对Thread.interrupted()可见
对象终结规则
对象初始化对对象的finalize()可见
AQS
JUC包下实现并发共享和互斥锁的底层机制,实现了比如CountDownLatch、Semaphore、ReetrantantLock等工具
它主要用来进行锁变量的维护,线程的等待和唤醒,锁公平非公平机制的实现
内部使用cas维护state变量进行加锁,用一个FIFO的双向队列进行等待加锁线程的管理
如果有必要,可以讲讲他公平非公平的实现机制:公平锁,当释放锁的时候,队列有等待线程则唤醒下一个节点的等待线程获取锁;非公平则是不管是否有等待线程,都会和新进来的线程进行获取锁的竞争
Spring
SpringBoot的自动装配机制了解?
作用
可以把依赖的第三方jar包的实例,自动注册到Spring的IOC容器中,不需要再自己手动配置
用法
在启动类上放上@SpringBootApplication注解,里面包含了@EnableAutoConfiguration注解,会负责启动配置类
核心技术
配置类必须包含@Configuration,里面用@Bean进行实例对象的声明
配置类是在第三方jar包的,它通过Spring的SpringFactoriesLoader加载Spring.factories,而配置类的全包路径就放在这个配置中
Spring拿到配置后通过ImportSelector接口对配置类进行动态加载
对Spring的理解
一个轻量级的框架,主要实现的功能为IOC和AOP
为web应用程序提供了MVC的机制
利用AOP的机制实现了很好的事务控制
具有完整活跃的生态,出问题也容易解决
对SpringIOC的理解?
IOC控制反转,原来由使用用控制对象的创建,改由Spring容器进行管理
DI依赖注入,将对象的属性通过注解@Autowired或@Resource进行属性注入
三级缓存
Spring的bean生命周期?
总体描述,bean对象从产生到销毁的环节都由容器进行控制,其中包括实例化和初始化两个主要步骤,当然中间会有一些扩展点的存在
描述具体步骤:
实例化对象,通过反射的方式,调用createBeanInstance方法生成对象
当bean对象创建完成,对象属性都是默认值,开始对bean填充属性,调用populateBean
向Bean对象中设置容器属性,会调用invokeAwareMethods方法将容器对象设置到具体的Bean对象中
调用BeanPostProcessor中的前置处理方法来进行Bean对象的扩展工作
调用invokeInitMethods方法来完成初始化方法的调用,如果实现了InitializingBean,调用afterPropertiesSet最后设置bean对象
调用BeanPostProcessor的后置处理方法,完成Bean对象后置处理工作,aop就是在此处实现的,实现接口名字叫AbstractAutoProxyCreator
获取完整对象,通过getBean的方式进行对象的获取和使用
当对象使用完成之后,容器关闭时,会销毁对象,判断是否实现DisposableBean接口,然后调用destroyMethod方法
BeanFactory和FactoryBean的区别?
BeanFactory和FactoryBean都可以创建对象,只不过创建的流程和方式不同,
当使用BeanFactory的时候,必须严格遵守bean的生命周期,经过一系列繁杂的步骤之后才可以创建出单例对象,是流水线式的创建过程
而FactoryBean是用户可以自定义bean对象的创建流程,不需要按照bean生命周期来创建,此接口包含三个方法,isSingleton:判断是否单例对象;getObjectType:获取对象的类型;getObject:在此方法中可以自己创建对象,使用new的方式或者代理的方式都可以,用户按照自己的需要随意去创建对象,在很多框架继承的时候都会实现FactoryBean接口,比如Feign
Spring用到哪些设计模式?
单例模式,spring中bean大都单例的
工厂模式:BeanFactory
模板方法:postProcessorBeanFactory
观察者模式:listener,event,multicast
适配器
Adapter
装饰者模式
BeanWrapper
责任链
aop有责任链模式
代理模式
aop动态
委托者模式
delegate
建造者模式
builder
策略模式
不同的加载方式,比如XmlBeanDefinitionReader,PropertiesBeanDefinitionReader
对三级缓存的理解?
解释什么是循环依赖?A依赖B,B依赖A
spring中bean对象的创建都要经历实例化和初始化也就是属性填充的过程
通过将对象的状态分开,存在半成品和成品对象的方式,来分别进行初始化和实例化,对应一级缓存中放成品对象,二级缓存放半成品对象
三级缓存value类型是ObjectFactory,是一个函数式接口,不是直接进行调用的,只有在调用getObject方法的时候才会去调用里面的lambda表达式,保证同名的bean对象只能有一个
JVM
GC的理解?
核心就是管理对JVM中垃圾对象的收集和销毁
组成
java堆
java虚拟机栈
局部变量表
操作数栈
方法返回地址
动态链接
方法区
程序计数器
本地方法栈
基础
JMM
java内存管理
背景
基于CPU的三层缓存机制,在并发请求的时候会导致缓存不一致性
怎么解决
通过总线锁或者缓存锁
总线锁是对CPU前端加锁,一旦获取到锁,其它CPU就要等待该CPU释放锁才能继续执行,效率比较低
缓存锁则是针对缓存行进行加锁,颗粒度更细,但带来一定的复杂度,最著名的缓存一致性协议就是mesi
怎么提升效率
加入了store buffer和invalid queue通过异步通知机制,进行更进一步优化
具体为当cpu更新数据的时候,会把更新数据先放入store buffer,在去异步通知其它cpu,其它cpu接到消息后也不是马上处理,而是放入invalid queue(失效队列),之后一个个执行缓存失效,目的当然后失效后重新从主内存加载最新的值。
但这里会有一个问题,在异步化后如何保证主内存拿过来的数据是最新的,这就是mesi协议来保证了,当修改cpu共享变量后,cpu状态会变为“修改”状态,这时候若发生cpu写共享变量操作,这里会触发一个写屏障,会立即把store buffer的数据全部写入主内容,保证后续操作是正确且最新的。cpu通知其它cpu后其它cpu会变成“失效”状态,这时候这些cpu发生读操作,会触发读屏障,那么会立即把invalid queue的数据全部执行完毕,也就是全部立即失效,保证之后是从主内存读到的最新数据。这样就保证的缓存的一致性
但这里会有一个问题,在异步化后如何保证主内存拿过来的数据是最新的,这就是mesi协议来保证了,当修改cpu共享变量后,cpu状态会变为“修改”状态,这时候若发生cpu写共享变量操作,这里会触发一个写屏障,会立即把store buffer的数据全部写入主内容,保证后续操作是正确且最新的。cpu通知其它cpu后其它cpu会变成“失效”状态,这时候这些cpu发生读操作,会触发读屏障,那么会立即把invalid queue的数据全部执行完毕,也就是全部立即失效,保证之后是从主内存读到的最新数据。这样就保证的缓存的一致性
mybatis
SqlSessionFactoryBuilder加载mybatis-config.xml配置,创建对应的SqlSessionFactory
SqlSessionFactory通过openSession获取sqlSession,SqlSession的创建会传入Exceutor交由它去执行
sqlSession通过getMapper获取MapperStatement,通过Executor创建的StatementHandler进行SQL的执行和入参,结果的处理
LinkedBlockingQueue和ArrayBlockingQueue的区别?
ArrayBlockingQueue采用数组有界列表,LinkedBlockingQueue是可以指定大小的双向链表的结构
ArrayBlockingQueue存取队列用的同一把ReentrantLock锁,LinkedBlockingQueue用putLock负责写入,takeLock负责读取,并发效率上会更高
两者的插入删除的开销不一样,ArrayBlockingQueue存取不会额外产生销毁对象实例,而LinkedBlockingQueue会创建额外的Node对象,更占用内存给gc带来压力
Spring事务失效的情况
1.mysql的myisam不支持事务
2.没有被Spring管理
自身调用自己方法
没加@Transactional注解
3.没有加事务管理器
4.异常吃掉没有抛出来
5.异常没有定义回滚,默认是RuntimeException
6.设置的事务传播类型是not_support
hash冲突的解决方案?
开放定址发
发生冲突,往后找可以放的空间
再hash法
发生冲突再hash
链地址法
类似hashMao,发生冲突加入链表
建立公共溢出区
发生冲突加入溢出区的空间
https
加密方式
通过TLS/SSL加密
什么是云原生?
大致可以概括为:容器化+微服务+devOps+持续交付+声明式API+服务网格
项目必问
你做的项目遇到过什么难点,怎么解决的?
0 条评论
下一页