事务的四大特性
原子性,隔离性,持久性,一致性
事务的隔离级别和现象
- 读未提交:可能产生脏读,读取到未提交的数据
- 读已提交:可能产生不可重复读取问题,A事务中读取到B事务已提交的数据,导致两次读取数据不一致
- 可重复读:可能产生幻读问题,A事务中查询到B事务插入的数据,导致两次查询条数不一致
- 可序列化:最高级别,强制事务序列化执行,独占锁,并发效率慢
不可重复读和幻读的区别
不可重复读的重点在修改,两次查询数据不同
幻读的重点在新增和删除,两次查询行数不同
Spring如何解决的循环依赖问题
首先说下什么是循环依赖:A对象依赖于B对象,B对象依赖于A对象。
Spring解决循环依赖的核心思想在于提前曝光,首先创建实例化A,并在三级缓存中保存实例化A的lambda表达式以便获取A实例,然后去实例化B,在实例化B的时候就可以取到A了,后面再去实例化A。
当我没有循环依赖和AOP时,这个三级缓存是没有在后续用到的。
线程有几个状态
6个状态分别是:新建,运行,阻塞,等待,超时等待,终止
wait和sleep的区别
- wait来自Object类,Sleep来自Thread类。
- wait会释放锁,sleep不会。
- wait必须在同步代码块中执行,sleep可以随便执行。
- wait可以捕获异常,sleep必须捕获异常。
synchronized和lock锁的区别
sync可重入锁,不可中断,非公平锁
lock可重入锁,可以判断锁,非公平锁(可设置)
什么是可重入锁
只要持有该锁,再次进入代码块就不需要争抢锁,如递归,可以一直进进出出,不会出现死锁
工作中遇到过的异常、
运行时异常,IO异常,数据库连接异常,ClassNotfound异常,OutOfMemoryError异常,ConcurrentModification并发修改异常
信号量和互斥锁的区别
信号量:一次可以被多个线程持有,资源调度
互斥锁:一次只能被一个线程持有,资源互斥
什么是AQS
抽象对象同步器,基于CLH队列(双向链表),用volatile修饰共享状态(state),线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒。
Executors线程池工厂的三大方法
newSingleThreadExecutor()单个线程池,newFixedThreadPool(n)创建固定大小的线程池,newCachedThreadPool()可伸缩的线程池
单例模式
饿汉式:直接创建对象。
懒汉式:使用时创建对象,双重检测锁和volatile关键字解决并发问题。
CAS(比较并交换—乐观锁)机制
比较当前工作内存中的值和主内存中的值,如果这个值是期望的,则执行操作。否则不执行。
存在ABA狸猫换太子问题:使用增加版本号可以解决,原子引用中含有乐观锁机制
怎么排查死锁问题
定位进程:jps -l
堆栈跟踪:jstack 进程号
JVM内存模型
- 堆:内存对象
- 栈:局部变量,方法出口,操作数,动态链接
- 方法区(元空间):常量,静态变量,类信息
- 本地方法栈:native方法
- 程序计数器:反编译代码的执行顺序号
JVM垃圾回收机制
JVM:年轻代内存满了之后会发生MINORGC,使用可达性分析算法,将引用的对象放入e0区,为引用的对象就进行minorgc垃圾回收,每发生一次minorgc,存活对象会在s0和s1区进行切换,当s区满了和分代年龄15,会进入老年代,老年代满了会发生fullgc,如果fullgc也释放不了对象,而新对象一直在产生并放入老年代就会内存溢出OOM。
JVM调优:判断该服务业务代码会执行多少秒*每秒产生多少M的对象,并设置年轻代Eden(8:1:1)的大小(业务代码刚好执行完成,发生minorgc进行回收),剩下的内存就分配给老年代。
G1收集器(C++语言实现),可以通过参数设置最大停顿时间,到达这个停顿时间就会发生gc,需要考虑业务场景,避免对象过大或者年龄太大,多数对象进入老年代。
JVM垃圾回收算法
- 引用计数法:程序计数器
- 年轻代-幸存者区:复制算法
- 老年代:
- 标记清除(标记存活的对象,没有标记的对象进行清除,会产生内存碎片)
- 标记整理(标记存活的对象,将存活的对象移动到一端,没有标记的对象进行清除,不会产生内存碎片)
为什么要设计STW(回收时停顿)
如果没有,fullgc和minorgc时会误删除程序中的非垃圾对象。
s区的大对象(大于50%)会直接放去老年代。
minorgc和fullgc会产生STW。
为什么堆里面要划分年轻代和老年代?
答:如果不划分的话,垃圾对象一直堆积不回收,等真正发生gc的时候,时间会超级长。
JMM内存模型
主要分为主内存(堆)和工作内存(栈),工作内存是主内存和一份拷贝,主要操作有:锁定lock,解锁unlock,读取read,载入load,使用use,赋值assign,存储store,写入write
TCP三次握手
二次握手的问题:客户端的第一次请求时延误发送到了服务端,服务端成功接收到了请求(但在客户端认为这个请求已经失败了),第二次响应时服务端向客户端响应,客户端认为该请求已失败,并不会接收这次响应,服务端会一直死死的等待(死锁)客户端发送数据报文,造成资源消耗(如果客户端重新建立连接,会建立一个新的通道,上一个通道会一直存在)。如果是三次握手的情况下,第三次握手如果服务端在一段时间内没接收到客户端的响应,就会关闭这个连接通道。
第一次握手,客户端向服务端发送请求建立连接。
第二次握手,服务端向客户端响应是否确认建立连接。
第三次握手,客户端会向服务端发送一个确认建立连接。
三次握手的目的:防止已失效的连接请求报文突然传送到了服务端
TCP四次挥手
由于TCP双向通道互相独立所导致的,
- c向s发送"我要关闭连接"。
- s向c发送"我知道了",并且s进入半关闭状态(可以接收数据,不可以发送数据)。
- s向c发送"我要断开连接了,你确认下",进入最后确认状态。
- c向s发送"可以断开",s接收到后会进行关闭连接,c等待2msl(数据报文一来一回的最大时间)后,进入关闭状态。
ArraysList扩容机制
默认初始容量10,扩容原始容量的1.5倍取整,扩容因子1
Vector向量扩容机制
线程安全sync锁
默认初始容量10,扩容原始容量的2倍取整,扩容因子1
HashMap扩容机制
默认初始容量16,扩容倍数2,扩容因子0.75
数组长度大于64并且链表长度大于7(每产生哈希冲突,进行equal比较,hash码相同替换,不同插入链表)->链表转变为红黑树,链表长度小于6则从红黑数转为链表,jdk1.7采用头插,jdk1.8采用尾插。
ConcurrentHashmap数据结构
1.7 采用ReentrantLock+Segment+HashEntry分段锁的思想,对每个Segment桶位加锁。
1.8 舍弃了分段锁的思想,更加接近hashmap,采用synchronized+CAS+HashEntry+红黑树,对每个数组元素加锁
并发的三大特性
原子性,可见性,有序性
双亲委派机制
类加载器:BootStrapClassLoader主加载器,ExtClassLoader扩展加载器,AppClassLoader系统类加载器以及线程上下文加载器,可以自定义加载器。
双亲委派:向上委派到顶层加载器,向下查找加载路径
守护线程
守护线程最后关闭,守护线程不可以用线程池创建,线程池创建的守护线程都会转换成用户线程,守护线程中创建线程,创建的还是守护线程
线程池执行流程
超过核心线程(甲方),就放入任务队列(排期),任务排不完,放入最大线程(外包),最大线程超过空闲时间,执行拒绝策略(辞退)
keep超时时间,最大线程中有线程空闲了,超过这个时间就回收线程
线程池七大参数
- corePoolSize 线程池核心线程大小
- maximumPoolSize 线程池最大线程数量
- keepAliveTime 空闲线程存活时间
- unit 空闲线程存活时间单位
- workQueue 工作队列
- threadFactory 线程工厂
- handler 拒绝策略
spring是什么
- 是一个轻量级的简化开发的框架,管理Bean的容器,各个框架的中间人(整合各种框架)
- ioc容器用于管理bean,使用aop解决oop代码重复的问题
- springmvc是spring对web框架的一个解决方案,总前端控制器servlet视图解析器生成视图到页面。
- springboot是spring的快速开发包,相当于spring和springmvc的整合,约定大于配置。
spring对象的生命周期
- 解析类得到BeanDefinition定义对象。
- 推断构造方法。
- 进行实例化,得到对象。
- 给对象中的@Autowired注解属性赋值。
- 回调Aware方法(获取容器,用于访问容器中其他的Bean)。
- 调用初始化前的方法。
- 调用初始化。
- 调用初始化后的方法。
- 将单例Bean放入单例池。
- 使用Bean。
- 销毁Bean。
bean的作用域
singleton单例,prototype多例,request一个请求一个单例,session一个session一个单例,application一个上下文一个单例,webSocket一个连接一个单例
spring的单例Bean是线程安全的吗
不是,框架并没有对多线程进行处理,使用双重检测锁和可见性关键字,ThreadLocal或者加锁。
spring的自动装配原理
@Import导入了扫描META-INFO/spring.factories文件,文件中配置了各个Starter中的配置类,配置类中使用@Bean注入,各个starter需要遵守springboot的约定
explain执行计划
主要优化看type字段,执行效率:避免(All和index) All全表<index遍历索引树<range检索范围数据<ref非唯一性索引扫描<eq_ref唯一性索引扫描<system表中只有一条数据<const通过索引一次命中,匹配一条数据
什么是MVCC机制
多版本并发控制的一种方法,通过版本链(readView维护活动的事务Id,排序成一个数组从小到大,数组外左边的是已提交的事务可以访问,数组外右边的是未提交的不可访问),实现多版本,可并发读写,通过readView,生成策略的不同,实现不同的隔离级别
RDB和AOP的区别
RDB:快照模式,通过配置文件save 600 3设置,600秒内至少有3个key发生修改,产生快照替换原有快照。
AOF:文件追加模式,记录修改动作到文件中,缺省一秒追加一次,appendfsync: 进行配置。
区别:AOF效率低于RDB,AOF采用追加RDB采用快照,RDB(如果死机后丢失未生成快照的数据)没有AOP(会丢失一秒内的数据,并不会影响之前存在的数据)数据安全
redis为什么采用单线程
只在处理网络请求时采用单线程,完成基于内存操作,非阻塞多路IO复用(使用select,可以建立多个socket连接处理IO非多线程),避免了上下文和多线程切换避免加锁产生问题引起资源消耗
什么是缓存雪崩、缓存击穿、缓存穿透?
缓存雪崩:缓存同一时间大面积失效,过期时间尽量间隔随机,缓存预热-启动时将数据同步到缓存。
缓存穿透:大量请求同时访问不存在的数据,都会到mysql查询,一般是受到了攻击,可通过缓存key-null➕过期时间,布隆过滤器,代码校验来解决。
缓存击穿:是指热点数据失效了,大量并发查同一条数据导致崩溃,可以通过热点数据永不过期-修改时同步缓存或者失效后数据库访问时加互斥锁
怎么实现接口幂等性(多次点击导致数据问题)
使用乐观锁机制,增加唯一业务流水号
HTTP和TCP的区别
HTTP位于应用层,http是基于TCP的一个短连接。
TCP协议位于传输层,TCP协议支持长连接和短连接。
redis基本数据类型
5种常见数据类型,string,hash,list,set,zset
mysql慢查询日志分析
可以通过命令或者my.cnf配置开启慢查询日志记录,mysql提供了工具mysqldumpslow可以进行分析
JVM锁升级
Java虚拟机对synchronized的优化,大多数情况下锁不存在多线程竞争,所以向第一个现场偏护,一旦出现多线程竞争的情况下,就会从偏向锁升级为轻量级锁(自旋锁),如果自旋执行了一定次数的CAS(比较并交换)还没有获取到锁,就会从轻量级锁膨胀为重量级锁,只允许一个线程进入
进程和线程的区别
进程是系统分配资源的最小单位,线程是系统调度的最小单位。
一个程序至少有一个进程,一个进程至少有一个线程。
pageHelper分页原理
使用拦截器拦截并重写SQL
mybatis #和$的区别
预编译,参数默认会加单引号,使用PreparedStatement的set方法赋值,用于取值场景,预防sql注入。
$ sql拼接,可用于动态表名/字段场景,会导致sql注入。
RabbitMQ
如何工作?
1. 生产者
- 创建交换机,指定交换机类型
- 创建队列
- 队列绑定交换机并可以指定routingKey路由键
- 发送消息到交换机并可以指定routingKey
1. 消费者
- 创建队列
- 监听队列
交换机各个模式区别和作用?
2. fanout,广播模式,发送消息到所有绑定的队列
3. direct,路由模式,发送消息到指定routingKey的队列
4. topics,主题模式,发送消息到指定routingKey(绑定时可以使用通配符)的队列
工作流程
- ConnectionFactory --> Connection --> Channel --> 创建交换机 --> 创建队列 --> 队列绑定交换机 --> 发送消息/接收消息
- 直接发送到队列:底层使用了默认交换机
常见故障的解决思路
服务器宕机消息丢失
消息队列默认是持久化到硬盘上的,不惧怕重启
标签:...,面试题,缓存,Java,对象,队列,线程,内存,客户端 From: https://www.cnblogs.com/star-blog/p/18464712