吐血整理B站java面试题:包含java基础、数据库、spring、springboot、spring cloud相关面试题,持续更新
-
- 事务的基本特性和隔离级别
- 如何进行慢查询优化:
- 怎么提高缓存的命中率?例如Redis缓存
- Buffer pool :缓冲池
- Innodb如何管理page页?
- Innodb是如何实现事务的?
- mysql缓冲区类型:
- 写缓冲区changeBuffer只适用于非唯一索引普通页?
- Mysql的lru算法为什么要改进(最近最少使用)
- 索引的原理:
- 使用索引一定会提升效率吗?
- 索引创建原则:
- 索引覆盖是什么?
- MySQL数据库page页分为三部分:
- Mysqld的聚簇索引和非聚簇索引:
- Mysql中索引使用B+树的特点
- Select,poll、epoll三种事件模型的区别
- Explain语句结果字段的含义:
- 如何连接mysql数据库服务?
- Mysql中锁有哪些?
- Mysiam和innodb的区别
- Java死锁如何避免?
- Java中实现多线程的方式?
- Java中线程的状态:
- 线程池执行任务为什么要添加一个非核心空任务的线程?
- 线程结束的方法:
- Java线程的wait和sleep方法区别
- 并发编程的三大特性:
- 线程池工作流程:
- 线程池的五个状态状态?
- Jdk自带的线程池构建方式一共五种:
- 如果提交任务时任务队列满了会发生什么?
- CAS是什么?
- AQS是什么?
- AQS是怎么实现重入锁的?
- CAS的缺点:
- Synchronized实现原理?
- Synchronized在jdk1.6的优化:
- 锁的分类:
- Jvm类加载机制:
- 双亲委派机制是什么?
- Jdk1.7和jdk1.8虚拟机的区别
- Java堆为什么要进行分块设计?
- 什么是老年代担保机制?
- 什么时候会发生垃圾回收?
- 垃圾回收算法?
- Jvm常用命令:
- Jvm性能解决方案:
- 垃圾回收器:
- 一个对象的生命周期:
- 分代回收算法:
- Jdk、jre、jvm区别
- Java中的四种引用类型
- 类型限定符中extend限定了类型的上限,super限定了类型的下限
- String、 stringbuffer 、stringBuilder区别
- ArrayList工作原理
- ArrayList和LinkList区别
- CopyOnWriteArrayList的原理
- Hashmap扩容原理
- ConcurrentHashMap的扩容原理
- ConcurentHashMap的计数器实现:
- HashMap或concurentHashMap获取数据的过程:
- 多叉搜索树和平衡多叉搜索树的区别?
- 一个对象从加载到jvm到被gc清除经历了什么过程?
- Springmvc工作流程:
- Bean的⽣成步骤如下:
- Spring三级缓存
- Spring容器启动流程:
- Spring事务的机制:
- Spring事务什么时候失效?
- Spring的两个特性IOC 和AOP
- Spring中事务是如何实现的?
- Springboot是什么?
- Spring和springboot程序启动的区别
- @ComponentScan注解作用
- @EnableAutoConfiguration注解作用:
- bootStrap.yml的作用
- Springboot优点?
- Springboot核心注解是哪个?
- Springboot自动装配的原理:
- Spring和springboot项目兼容:
- Springboot有哪几种读取配置的方式
- Springboot支持哪些日志框架?
- SpringBoot springmvc 和spring的区别?
- Springboot打包的jar包和普通jar包有什么区别
- SpringBoot的run方法做了什么?
- Springboot如何解决跨域问题?
- Springboot如何配置log4j日志
- Springboot如何实现定时任务?
- Springboot有哪些核心配置文件?
- Springboot中常用的starter
- 如何在springboot启动的时候运行一些特定代码?
- Spring Cloud的一些常用组件
- Nacos的服务注册表结构是怎样的?
- nacos是如何支持数十万服务注册压力的?
- Nacos和eureka的区别有哪些?
- 服务熔断和降级组件Hystix和sentinel的区别
- Sentinel和gateway的限流有什么区别?
- Mybatis的优缺点
- 什么是微服务?
- 服务限流的算法?
- 如何设计一个高并发的系统?
- 如何处理生产者的确认消息:
- Freemarker使用场景:
- 服务负载均衡算法有哪些?
- 缓存雪崩、缓存穿透、缓存击穿分别是什么?如何处理?
- Redis过期键的删除策略:
- Redis线程模型,单线程快的原因:
- Redis有四种集群策略:
- Redis 主从同步原理:
- 浏览器发送一个请求到接收到响应有哪些步骤?
- 服务熔断和服务降级是什么?
- 什么是服务雪崩和服务限流
- 分布式架构下session共享的实现方案
- 工作中用到的一些微服务技术
- 分布式锁:
- 分布式事务解决方案:
- 如何实现接口幂等性:
- Spring cloud和dubbo的区别:
- Hystrix熔断和降级:
- 使用mq异步调用的优缺点:
- 使用mq的时候如何保证消息的可靠性?
- Rabbit MQ的架构设计:
- Rabbitmq是如何确认消息发送的?
- Rabbitmq的死信队列和延时队列的了解
事务的基本特性和隔离级别
事务的基本特性ACID
原子性:一个事务中的操作要么全部成功,要么全部失败
一致性:
隔离性:一个事务在提交之前,对于其他事务是不可见的
持久性:事务一旦提交,所做的修改会永久保存到数据库中
事务的隔离级别和存在的问题:
读已提交:不可重复读,两次读取的结果不一致
读未提交:脏读,读到其他事务修改了但是没有提交的数据
可重复读:每次读取的数据相同,可能产生幻读,即与数据库数据修改后数据不一致
串行:不能并发,会给每一行上锁
如何进行慢查询优化:
MySQL可以开启慢查询日志,日志中会记录到慢查询对应的sql语句和执行时间等信息,对于经常访问的数据需要进行优化
优化思路:
1、使用explain 进行分析,添加合适的索引
2、检查所使用的字段是否必须,未使用到的字段不需要查询出来
3、检查group by 等函数可能会导致索引失效
4、关联多个表时,可能会导致查询缓慢,建议不超过三张表关联查询
5、检查所使用的索引是否为最优索引,可以指定使用哪个索引
6、检查表中数据是否过多,过多可以考虑分库分表
7、检查服务器性能配置,优化服务器性能
怎么提高缓存的命中率?例如Redis缓存
1、将缓存空间扩大,能够存储的缓存数据更多
2、提高缓存的更新频率
3、提前将缓存数据加载
4、调整缓存的存储类型,可以使用多个字段进行缓存
什么是索引下推?
在执行辅助索引的时候,先执行where语句中的内容,筛选数据,减少回表的次数,
需要数据库进行配置
Buffer pool :缓冲池
不直接查询数据库,减少io操作,先在缓冲池中查询符合条件数据
缓冲池由缓存数据页和缓存控制块两个组成,控制器保存了缓存所属的表空间和数据页编号等信息,page页默认占16k,控制块占0.8k
Mysql中有一个hash表,通过表空间+数据页号这个key,可以拿到对应的控制块,就能判断一个页是否在缓冲区
Innodb如何管理page页?
Page页有三个类型,都是用的链表进行存储
Free page 未使用的page
Clean page 使用了但是没有修改的page
Drity page 已经被修改了的页,需要在空闲的时候持久化,刷到磁盘中
Innodb是如何实现事务的?
Innodb是通过buffer pool 、logBuffer、redolog、 undolog等组件实现事务的,先在缓冲池中更新,事务提交后再将缓冲池中的数据刷新到数据库中,以update语句为例:
1、innodb在收到一个update语句后,会先找到数据所在的page页并将该页缓存到buffer pool中
2、执行update语句,在内存中修改buffer pool的数据
3、针对该update语句生成一个redolog对象,并存入到logbuffer中,也是在内存
4、对该语句生成一个undolog对象,用于事务回滚
5、如果事务提交,则将redolog对象进行持久化,后续有其他机制将buffer pool中的数据刷到磁盘中持久化
6、如果事务回滚,则利用undolog对象进行回滚
mysql缓冲区类型:
Free list 空闲缓冲区 管理free page
Flush list 表示需要刷新到磁盘的缓冲区 管理drity page
Lru list 表示正在使用的缓冲区,管理 clean page和 drity page
写缓冲区changeBuffer只适用于非唯一索引普通页?
changeBuffer是缓冲区Buffer pool中的一部分 数据修改后先在changeBuffer更新到Buffer pool ,再由Buffer pool 缓存到磁盘中。
因为唯一索引会需要判断数据唯一性,直接去数据库中查询,不会在缓冲区查询
Mysql的lru算法为什么要改进(最近最少使用)
尾部淘汰制:新数据添加到链表头部,被使用到的数据页添加到链表头部
缺点: 如果全表扫描,则会把热点数据刷到链表后面被淘汰
改进后将数据分为冷数据区和热数据区 热数据区占63%,第一次查询的数据替换到冷数据区头部midpoint处,如果存活时间超过1s时放到热数据区头部
索引的原理:
索引就是将无序的数据进行有序的排列存储,使用B+树(二叉平衡树)的数据结构存储索引,即倒排表
1、把创建了索引的列的内容按照btree算法,进行排序
2、对排序结果生成一个倒排表,并维护到数据库中
3、在倒排表内容上拼接数据真实的地址链
4、查询的时候先通过索引拿到倒排表上的数据,再通过地址链拿到具体的数据
使用索引一定会提升效率吗?
使用索引不一定会提升效率
1、索引会占用物理空间,
2、对表数据的修改,索引也需要动态维护
3、索引字段过多后会影响查询速率(5个字段)
索引创建原则:
索引的原理:将无序的字段进行有序的排列,就不用查询全表
1、在经常使用到的字段上创建索引
2、在主键和外键上创建索引
3、在排序字段上创建索引
4、在经常使用到的where条件字段上创建索引
5、对于频繁修改的列不要创建索引,修改数据索引也要更新
6、重复数据太多,字段区分度不高的字段不适合创建索引
7、尽量扩展索引而非新增索引
索引覆盖是什么?
索引覆盖是指我们查询的数据字段在索引中都存在了,可以直接返回数据,而不需要再去回表通过主键id查询
MySQL数据库page页分为三部分:
通用部分,数据记录部分和数据目录部分。
Mysqld的聚簇索引和非聚簇索引:
聚簇索引的主键索引数据和表数据是同一份数据,辅助索引中只存储主键值,再通过主键值去主键索引数据中查询
非聚簇索引则在索引记录中不保存表数据,只保存主键值,需要再通过主键和地址链去数据表中进行查询
聚簇索引比非聚簇索引查询效率高,但是更新数据时效率比较低
Mysql中索引使用B+树的特点
B+树是对B树的升级,B+树的非叶子节点存储的是索引数据,叶子节点是存储的表的所有数据 ,B+树叶子节点之间有指针指向子节点
非叶子节点的数据都会在叶子节点上冗余,也就是叶子结点存储了所有元素,且排好序了
Mysql一页可以存储16k数据,两层B+树可以存储2000万行数据,超过这个值会增加层数,导致查询效率变低,可以考虑分库分表。
Select,poll、epoll三种事件模型的区别
Select模型:使用数组存储socket连接文件描述符,容量固定,需要轮询判断是否发生io事件,遍历数组里面的socket连接,查看是否需要读取文件。
Poll模型:使用链表保存socket连接文件描述符,容量不固定,也需要轮询判断是否发生io事件
Epoll模型:是一种事件通知模型,当发生了io事件时,程序才进行io操作,不需要主动轮询,效率比较高
Explain语句结果字段的含义:
如何连接mysql数据库服务?
1、引入数据库连接的依赖
2、去除默认的h2数据库依赖
3、在application.properties文件中进行数据库连接配置。
Mysql中锁有哪些?
按锁的粒度进行区分:
1、表锁:整个表都被锁住,其他线程不能读写该表,并发低
2、行锁:只锁某一行记录,粒度细,并发高
3、间隙锁:粒度适中,锁的是两行记录中间的记录区间
还可以分为共享锁(读锁)和排他锁(写锁)
一个事务给某行加了读锁,其他事务可以读,但是不能写
一个事务给某行加了写锁,其他事务不能进行读写操作
还可以分为悲观锁和乐观锁,上面的行锁和表锁都是悲观锁
Mysiam和innodb的区别
Mysiam:
1、不支持事务
2、只支持表级锁
3、会存储数据总行数
4、主键索引采用非聚集索引,索引文件的数据域存储指向数据文件的指针,辅索引和主索引基本一致,只是辅助索引不需要保证唯一性
Innodb:
支持ACID事务
支持行级锁及外键约束,可以写并发
不存储数据总行数
主键索引采用聚集索引(索引的数据域存储数据文件本身),辅助索引存储主键值
在读写分离的系统中,从数据库只需要读操作,可以使用mysiam,同步数据比较快。
Java死锁如何避免?
Java死锁需要满足四个条件
1、一个资源只能被一个线程使用
2、一个线程在阻塞等待资源时,不释放已占用的资源
3、一个线程已经获得的资源,不能被其他线程强制剥夺
4、若干线程形成了首尾相连的循环等待资源状态
前三个是线程基本特性,没法改变,只有第四个需要注意
1、注意加锁的顺序,两个线程都先加锁a再加锁b
2、注意加锁的时限,设置超时时间
3、注意死锁检查,是一种预防机制,使用jstack命令可以查看java线程是否死锁
Java中实现多线程的方式?
一共四种
1、继承Thread类,重写run方法
2、实现Runable接口,实现run方法
3、实现callable接口,实现call方法,会返回一个FutureTask ,可以拿到线程的返回值
4、使用线程池
其实本质上都是实现runable接口
Java中线程的状态:
一共有六种状态
new 一个线程得到新建状态(NEW)的线程,
执行start方法得到一个就绪状态(RUNNABLE),
当调用wait方法时,线程变为等待状态(WATING)需要手动唤醒,
调用sleep方法得到时间等待状态(TIMED_WATING)时间到了后变为就绪,
当线程竞争锁失败是阻塞状态(BLOCKED)
线程执行结束变成结束状态(TERMINATED)
线程池执行任务为什么要添加一个非核心空任务的线程?
线程池的核心线程个数允许设置为0,当任务进来时如果不添加线程,就没有线程可以执行任务,线程池还可以设置核心线程超时关闭属性,设置为true时,核心线程会被干掉,可能会导致没有线程可用,需要添加非核心空任务的线程处理。
线程结束的方法:
1、调用stop方法,线程会立即停止
2、Interrupt方式,会有一个中断标记位thread.Interrupt会将线程的中断标记位的值改为true,默认是false
3、共享变量:需要在线程执行方法里面留下判断终止的条件判断
Java线程的wait和sleep方法区别
1、sleep方法属于Thread类里面的方法,wait是Object的方法
2、Sleep不会中断锁,不需要手动唤醒,wait会中断持有的锁,需要调用notify方法唤醒
3、Sleep可以在不持有锁的时候执行,wait需要持有锁才能执行
并发编程的三大特性:
原子性:线程运行过程中,其他线程不会对当前线程造成影响
保证原子性:使用synchronized关键字、使用CAS,使用lock锁,使用ThreadLocal保证操作的是当前线程的资源
可见性:在一个线程对共享资源的操作对于其他线程来说是可见的
保证可见性:使用volatile修饰,修改后会同步到内存,使用synchronized关键字、使用lock锁,使用final修饰
有序性:多线程执行不影响最后的结果,防止指令重排
线程池工作流程:
添加任务到线程池–》判断任务是否为null–》判断是否还有核心线程未创建,有未创建的核心线程则会先创建核心线程,没有核心线程未创建的话–》判断线程池是否异常,将任务添加到阻塞队列中–》如果线程池状态不正常或添加阻塞队列失败,则判断是否添加非核心线程处理,如果添加不了,就执行淘汰策略
线程池的五个状态状态?
New一个线程池后,线程为running状态,可以处理任务,
调用shutdown()方法后,为shutdown状态,线程不会接受新任务,运行中的和阻塞队列中的任务会执行结束
调用shutdownNow()方法后,线程是stop状态、不会接受新任务,且运行中的任务也会被中断
调用上面两个方法后,线程队列中没有任务就变成tidying这个过渡状态
tidying状态执行terminated()方法线程编程terminated状态,销毁状态。
Jdk自带的线程池构建方式一共五种:
1、newfixedThreadPool 固定线程的线程池,创建的线程都是核心线程,但是任务队列无界
2、NewSingleThreadPool 单例线程池,只要一个线程执行任务,任务执行有顺序,但是任务队列无界,任务太多可能会导致任务积压,内存溢出
3、NewCachedThreadPool 缓存线程池,没有核心线程,每次执行任务都要新建线程,执行效率低,当任务太多的时候会创建很多线程,会内存不足
4、NewScheduleThreadPool 定时任务线程池,可以延迟或定期执行任务
5、NewWorkStealingPool 其他线程都只有一个阻塞队列,他的每一个线程都有一个队列,当前线程队列任务执行完后会去其他线程队列中获取任务执行
都会有一些不足,所以阿里巴巴建议自定义创建线程池,可以配置不同的任务队列类型
如果提交任务时任务队列满了会发生什么?
如果是无界队列,那么可以无限提交任务
如果是有界队列,会先判断线程池是否有核心线程未被创建,如果有的话新增一条核心线程处理当前任务,如果没有的话会使用拒绝策略进行拒绝。
ReentrantLock和synchronized 区别
ReentrantLock是java类,synchronized是一个关键字
Synchronized作用于对象和方法,类上,ReentrantLock是作用于代码块
Synchronized不需要手动释放锁,ReentrantLock需要手动lock()加锁,unlock()解锁
Synchronized只支持非公平锁,reentrantLock支持公平锁和非公平锁,调用构造函数传参
Synchronized引入了锁升级后两者效率差不多,Synchronized使用比较便捷
CAS是什么?
比较和替换,当要修改某个值时,先拿修改前的值和内存中的值进行比较,如果和预期值相同,说明没有其他线程对数据修改,则对数据进行修改,如果和预期值不同,则不进行修改,说明已经有其他线程修改了,可能有ABA问题,可以增加版本号解决。Cas自旋过多可能会占用CPU资源,可以指定自旋次数,超过后线程挂起
AQS是什么?
AQS是一个抽象类AbstractQueuedSynchronizer,ReentrantLock和线程池、阻塞队列,seamphor中有用到,里面维护了一个volatile修饰的int类型的state,用于判断资源是否被线程持有,大于0说明被持有,还有一个双向链表,用于存储等待锁的线程,线程作为节点Node存入。
AQS是怎么实现重入锁的?
内部维护了一个state属性记录锁重入次数,维护了一个双向链表队列存储等待锁的线程,
在加锁的时候,会判断要加锁的线程和当前持有锁的线程是否同一个,是的话state加1,否的话加锁失败,在链表队列等待
CAS的缺点:
1、ABA问题:添加版本号
2、自旋时间过长会影响效率:限定最大自旋数,超过挂起线程
3、范围不能灵活控制:多个类型的变量组成一个新的变量
Synchronized实现原理?
Synchronized修饰对象,新建对象时在内存中会有保存对象头等信息,对象头有个属性markword,在无锁或偏向锁时后三位表示锁状态,轻量级锁和重量级锁在最后两位保存锁的状态信息。
Synchronized在jdk1.6的优化:
锁消除:加了锁和不加锁对代码没有影响,如非多线程代码块,jit会