java 面试

java面试题

spring中有哪些方式可以把Bean注入到IOC容器

好的,把bean注入到IOC容器里面我知道有7种方法
1.使用xml的方式生命Bean的定义,Spring容器在启动的时候会加载并解析这个xml,把bean装在到IOC容器中.
2.使用@CompontScan注解来扫描声明@controller @service @Repository @componet注解的类 然后把这些类加载到IOC容器里面
3.使用@configuration注解声明配置类,并使用@bean注解实现bean定义,
这种方式其实就是xml配置方式的一种演变,是spring迈入到无配置化时代的里程碑
4.使用@import注解,导入配置类或者普通的bean
5.使用factorybean工厂bean,动态构建一个bean实例,springcloud openfeign里面的动态代理实例就是使用Factorybean来实现的
6.实现importbeandefinition-Registrar接口,可以动态注入bean实例,这个在springboot里面的启动注解有用到
7.实现importselector接口,动态批量注入配置类或者bean对象,这个在springboot里面的自动个装配机制里面有用到

项目的技术架构是怎么设计的

springcloud alibaba这么一套组件
nacos做分布式注册与配置中心
openfeign+ribbon 做远程RPC调用
hystrix做服务熔断机制
gatewat做网关
springSecurity+Oauth2.0协议做一套分布式认证服务
xxl-job 分布式定时任务组件
基于脚本的调用的自动化运维的中间件 saltstack 部署在K8S容器中,能操作这些容器中的这些mysql

定时任务的重复和调度是怎么解决的

调度重复的话,本身xxl-job他本身是自带一个基于DB的这种分布式锁,但是还不太够,这里要用redission分布式锁来实现,他有一个看门狗的机制,本来要设置30秒的超时时间,你就会每10秒钟去看一次,看这个程序有没有跑完,没跑完就继续,给你的这个锁的key继续续期,框架已经实现好了.

延迟任务怎么样去存储和运行

redission里面运用了一个时间轮的机制来实现的

springCloud alibaba是怎么理解的

基于spring cloud netflix 的进一步改良,比如nacos替换了eureka
有自己的生态比如有nacos注册中心,配置中心,sentinel做服务的限流熔断,seata做分布式事务,

nacos的动态更新怎么实现的

通过长轮询的定时机制去拉取nacos上这些的配置文件,然后去看他的MD5的这个编码,知道他的这个配置文件是不是有新的变化,新的更新

mysql为什么用b+tree作为索引结构

关于这个问题我会从几个方面来回答,首先常规的数据库存储引擎,一般都是采用B树,或者B+树来实现索引的存储,因为B树呢是一种多路平衡树,用这么存储结构来存储大量数据的情况下呢,他的整体高度相比二叉树来说,会矮很多,而对于数据库来说,所有数据必然是存储在磁盘上的,而磁盘IO的效率是很低的,特别是在随机磁盘IO的一个情况下,效率更低,所以树的高度就决定了磁盘IO的一个次数,磁盘IO次数越少,对性能的提升就会越大,这也是为什么采用B树作为索引存储结构的原因.
但是在mysql的innodb存储引擎里面他采用的是一种增强的B树结构,也就是B+树,相对于B树,B+树做了这个几个优化,首先是
1.B+树的所有结构数据都存放在叶子节点上,而非叶子节点只会存储索引
2.叶子结点中的数据使用双向链表方式进行关联
所以我认为用B+树作为索引结构有以下几个原因
1.B+树非叶子结点不存储数据,所以每一层能够存储的索引数量增加,意味着B+树在层高相同的情况下存储的数据要比B树要多,使得磁盘IO次数更少
2.mysql中,范围查询是一个比较常用的操作,而B+树的所有存储在叶子结点的数据使用了双向链表来关联,所以在查询的时候只需查两个结点进行遍历就行,而B树需要获取所有节点,所以B+树在范围查询上效率更高.
3.在数据检索方面,由于所有的数据都存储在叶子节点,所以B+树的IO次数会更加稳定一些
4.因为叶子结点存储所有数据,所以B+树的全局扫描能力更强一些,因为他只需要扫描叶子结点,但是B树需要遍历整个树
另外基于B+树这样一个结构,如果采用自增的整形数据作为主键,还能更好的去避免增加数据的时候,带来的叶子结点的分裂,导致大量运算的一个问题

reentrantLock的实现原理

reentrantlock是一种可重入的排它锁,他主要是解决多线程对于共享资源的竞争的一个问题,他的核心特性有
1.他支持可重入,也就是获得锁的线程在释放锁之前再次去竞争同一把锁的时候,不需要加锁就可以直接访问
2.支持公平和非公平的特性.
3.他提供了阻塞竞争锁和非阻塞竞争锁的两种方法,分别是lock(),trylock()
他底层用到了几个非常关键的技术
1.锁的竞争,利用互斥变量,使用CAS机制来实现的
2.没有竞争到锁的线程啊 abstractQueueSybchronizer这样一个队列同步器来存储,底层是通过双向链表来实现,当锁被释放之后呢,会从AQS队列里面的头部,去唤醒下一个线程
3,公平和非公平的一个特征,主要是体现在竞争的时候,公平锁是否需要判断AQS队列里面是否有等待线程
而非公平锁是不需要去判断的
最后关于锁的重入性,在AQS里面有一个成员变量来保存,当前获取锁的线程,当同一个线程,再次来竞争同一把锁的时候,那么不会去走锁的竞争逻辑,而是直接去增加重复次数.

数据库连接池他有哪些关系参数

数据库的连接池是一种池化技术,池化技术的核心思量是实现资源的一个复用,避免资源的重复创建和销毁,带来的一个开销,而在数据库的应用场景里面呢,应用程序每一次向数据库发起CRUD操作的时候,都需要去创建连接,而在数据库访问量比较大的情况下呢,频繁的创建链接会带来比较大的性能开销,而连接池的核心思想就是,应用程序在启动的时候,去提前初始化一部分的链接,保存在连接池里面,当应用程序需要用到连接去进行数据操作的时候,直接从连接池里面取出一个已经建立好的链接,进行操作就好了.所以连接池的设计避免了每一次连接的建立和释放,带来的一个开销.
连接池初始化关键参数:
1.初始化连接数,表示启动的时候初始多少个连接保存到连接池里面
2.最大连接数,表示同时最多能支持多少连接,如果连接数不够,后续要获取连接的线程会阻塞
3.最大空闲连接数,表示没有请求的时候,连接池中要保留的最大空闲连接.
4.最小空闲连接,当连接数小于这个值的时候,连接池需要再创建链接来补充这个值
连接池使用时的关键参数:
1.最大等待时间,就是连接池里面的链接用完了,新的请求要等待的时间,超过这个时间就会提示超时异常.
2.无效连接清除,清理连接池里面的无效连接,避免使用这个连接操作的时候出现错误

线程池的理解

线程池本身就是一种池化技术,池化技术是资料复用的一种思想,比较常用的池化技术有连接池,内存池,对象池.
而线程池复用的是线程资料
他的核心设计目的我认为有2个
1.减少线程的频繁创建和销毁带来的性能开销,因为线程创建会涉及到CPU上下文切换,内存分配等工作
2.线程池本身会有参数来控制线程创建的数量,这样就可以避免无休止的创建线程带来的资源利用率过高的问题,起到了资源保护的作用
而实现了线程的复用,线程池用到了阻塞队列,线程池里面的工作线程处于一直运行状态,它会去从阻塞队列里面去获取待执行的一个任务,一旦队列空了,那么这个工作线程就会被阻塞,直到下一次有新的任务进来,工作线程是根据任务的情况来决定阻塞或唤醒,从而去打到线程复用的一个目的.
线程里面的资源控制是通过几个关键参数来确定的
核心线程数 默认长期存在的工作线程
最大线程数 根据任务的情况来动态创建的线程,提高阻塞队列中任务的处理效率

mysql的事务隔离级别

事务的隔离级别是为了解决多个并行事务竞争导致的数据安全问题的一种规范,具体来说多个事务竞争可能会产生三种不同的一个现象
1.假设两个事务,T1 T2同时在执行,那么T1事务可能读取到T2事务未提交的数据,但是未提交的事务T2有可能会产生回滚,也就是导致T1事务读取到一个最终不一定存在的数据,从而产生一个脏读的现象

2.假设两个事务,T1 T2同时在执行,那么事务T1在不同的时刻读取的同一行数据的时候,有可能结果会不一样,从而导致一个不可重复读的一个问题.(修改)
3.假设两个事务,T1 T2同时在执行,事务T1在执行范围查询的时候,或者范围修改的时候,事务T2呢插入了一条属于事务T1范围内的数据,并且提交了,那么事务T1查询的时候发现多了一条数据,或者T1事务发现这条数据并没有被修改,看起来产生了幻觉,我们称之为幻读.(增加)

在实际应用里面呢,可能有写场景不能接受,某些现象的存在,所以在SQL标准里面定义了四种隔离级别
1.读未提交 可能会产生脏读,不可重复读,幻读
2.读已提交 可能会产生不可重复度和幻读
3.可重复读 可能产生幻读
4.串行化 多个并行事务串行化执行,不会产生安全性问题 性能最低
mysql 默认隔离级别 RR(可重复读)

什么是可重入锁

可重入是多线程并发里面的一个概念,在运行的某个函数或者代码,因为抢占资源或者中断,导致这个函数或者代码运行过程中被中断了,那么等到中断的程序执行结束以后,重新进入到这个函数代码里面,在运行的时候,并且运行的结果不会发生变化.那么这个函数或者代码就是可重入的
可重入锁:
简单来说就是一个线程,如果抢占到了互斥锁的资源,在锁释放之前,再去竞争同一把所的时候,不需要等待,只需要去记录重入次数.
绝大多数的锁都是可重入的,比如说synchronized,reentrantLock
不支持重入锁的有 stampedLock
锁的可重入性主要解决问题是避免死锁的问题
因为一个获得同步锁的X的线程,在释放X之前再次去竞争锁X的时候,相当于等待自己的一个锁释放的一个情况,这很显然无法成立,会导致死锁.
以上就是我对这个问题的一个理解

spring中beanFactory和FactoryBean的区别

1.breanfactory是所有springbean容器的顶级接口,他为spring的容器定义了一套规范,并提供像getbean这样的方法从容器中获取指定的bean的实例
2.beanFactory在产生bean的同时,还提供了解决bean之间的依赖注入的能力,也就是所谓的DI.
而factorybean是一个工厂bean,他是一个接口,他的主要功能是动态去生成某一类型bean的一个实例,也就说我们可以自定义一个bean并且加载到IOC容器里面,他里面有一个重要的方法getObject(),这个方法就是用来实现动态构建bean的一个过程.
springcloud里面openfeign的组件,客户端代理就是使用了factorybean来实现的

redis存在线程安全问题吗

从redis服务端层面,redisserver本身是一个线程安全的K-V数据库,redis-server去执行指令的时候不需要任何的同步机制,不会存在线程安全的问题
redis6.0里面加入的多线程模型,只是去处理网络IO事件,对于指令的执行过程仍然是采用主线程来处理,不会存在多个线程同时去操作一个指令的情况.
redisserver没有采用多线程来执行指令,我认为呢
1.redis server本身可能出现的性能瓶颈点无非就是网络IO,CPU,内存.但是CPU不是redis的瓶颈点,所以没必要使用多线程来执行指令.
2.如果采用多线程,意味着对于redis的所有指令操作,都必须要考虑到线程安全问题,也就是说需要枷锁来解决,这种方式带来的性能影响反而更大
从redis客户端来说
虽然redis server中的指令操作是原子的,但是如果有多个redis客户端同时执行多个指令的情况下呢,就无法无保证原子性了
假设有2个redisclient同时去获取redis server上的key1时,同时去进行修改和写入,因为多线程下的原子性无法被保障,以及多进程的情况下共享资源访问的一个竞争问题,使得数据的安全性无法得到保障
客户端的线程安全性的问题解决方法有
1.尽可能使用redis的原子指令
2.对于多个客户端的资源访问去加锁
3.通过lua脚本来实现多个指令的执行

springbean的作用域有哪些

1.singleton 单例 意味着整个spring容器中只会存在一个bean实例
2.prototype 原型 意味着每次从IOC容器去获取指定bean的时候,都会返回一个新的实例对象
但是在基于spring框架下的web应用里面
增加了一个会话维度来控制bean的声明周期这样一个功能
1.request
针对每一次http请求都会创建一个新的bean
2.session
以session会话为维度,同一个session共享同一个bean实例,不同的session产生不同的bean实例
3.globalsession
针对全局session维度共享同一个bean实例

mybatis里面的缓存机制

mybatis设计了一个二级缓存来提升数据检索效率,避免每一次都去查询数据库
一级缓存是sqlsession级别的一个缓存,也叫本地缓存,因为每一个用户在执行查询的时候,都需要使用sqlsession来执行
避免每一次都去查询数据库,mybatis把查询出来的数据缓存到sqlsession的本地缓存里面,后续的sql如果在命中缓存的情况下,就可以直接从本地缓存里面去取了
跨sqlsession这样的一个查询,一级缓存是无法做到的,所以mybatis设计了一个二级缓存,也就是说当多个用户在查询数据的时候,只要有任何用户数据拿到了sqlsession的数据,就会放入到二级缓存里面,那么其他的sqlsession就可以直接从二级缓存里面去加载数据了
一级缓存原理:
在sqlsession里面会持有一个executor,每个executor里面会有个叫localcache的一个对象,当用户发起查询的时候,mybatis会去根据执行语句,在localcache里面去查,如果命中了就把数据返回,如果没有命中再去数据库里面去查询出来,再写入到localcache里面
所以一级缓存的生命周期为sqlsession,所以在分布式环境或者在多个sqlsession的环境下,可能会因为一级缓存造成脏读的问题
二级缓存原理:
在原理的executor上面做了一个装饰,引入了cachingexecutor这样的一个装饰器,所以在进入一级缓存的查询之前呢,会先通过cachingexecutor进行二级缓存的查询,开启二级缓存之后,会被多个sqlsession共享,所以她是一个全局的缓存,所以她的查询流程为先查二级缓存,再去查一级缓存,然后再去查数据库
二级缓存要实现实体类的序列化

spring中事务传播行为

事务的传播行为是指多个事务在相互调用的时候,事务去如何传递
比如说methodA()去调用methodB(),事务怎么去执行,就取决于事务传播的一个行为
spring定义了7中传播行为
required 默认 如果说当前存在事务,就加入到这个事务里面去执行,如果不存在事务,就新建一个事务
required_new 不管存不存在事务,都会新建一个事务去执行,新老事务是相互独立的,外部事务抛出异常,并不会影响内部事务的一个正常提交
nested 嵌套 如果当前存在事务,就嵌套在当前事务中去执行,如果当前没有事务,就新建一个事务
supports 支持 表示支持当前的事务,如果当前不存在事务,就以非事务的方式去执行
not supports 表示以非事务的方式来运行
如果当前存在事务,则需要把当前的事务挂起来
mandatory 强势 强势事务的执行,当前不存在事务就抛出一个异常
never 以非事务的方式来执行,如果当前存在事务则抛出异常

spring框架的看法

spring是一个轻量级的框架,提供了IOC和AOP这两个核心功能,想让开发者更关注业务的需求,不需要关心bean的一些管理,以及通过切面的方式对功能进行增强,从而去减少代码的侵入性

谈谈你对线程安全的理解

线程安全表现有三个方面
1.原子性 2.有序性 3,可见性
原子性:一个线程执行一系列的的程序指令的操作的时候,他是不可中断的,一旦中断会出现前后数据不一致的结果
CPU的上下文切换是导致原子性的一个核心,而JVM提供一个synchronized关键字来解决
可见性:多线程环境下,读和写是发生在不同的线程里面的,有可能会出现某个线程对共享变量的修改,对其他线程不是实时可见的
导致原因有:CPU的高速缓存,CPU的指令重排徐,编译器的指令重排序
有序性:程序编写的指令顺序和CPU编写的指令顺序可能会出现不一致的现象,这种现象可以程之为指令重排序
可见性和有序性可以通过JVM提供的volatile这个关键字来解决

什么是聚集索引和非聚集索引

聚集索引就是基于主键创建的索引,除了主键以外的其他索引统一称为非聚集索引,也叫二级索引
聚集索引就是按照每张表的主键来构建这样一个B+树,然后叶子结点里面存储了这个表里面的每一行数据记录,所以基于Innodb这样的一个特征呢,聚集索引并不仅仅是一种索引类型,还代表了一种数据的存储方式.
同时意味着一张表里面必须要有一个主键,如果没有这个主键,Innodb会默认选择或添加一个隐藏列,作为主键索引来存储这个表的数据行
一般情况是建议使用自增ID作为主键,这样写入和检索性能都很高,如果是使用UUID这种随机id在频繁的插入数据的时候就会导致随机磁盘IO,从而会导致性能下降.
Inoondb里面只能存在一个聚集索引,由于主键索引是存储了一个表的完整数据,如果是基于非聚集索引来查询一条数据的时候,那么最终还是得访问主键索引来进行检索

ArrayBlockingQueue的原理

阻塞队列呢是在队列的基础上增加了两个附加操作
第一个是在队列为空的时候获取元素的线程会等待队列变为非空
当队列满的时候,存储元素的线程会等待队列为可用
这样就可以非常容易去实现生产者与消费者这样的一个模型
如果队列满了,生产者就等待,队列空了,消费者也需要等待
用到两个关键的技术
1.队列元素的存储
2.线程阻塞和唤醒
ArrayBlockingQueue是基于数组结构的阻塞队列,队列的元素是存储在数组结构里面,并且由于数据的长度是有限制的,为了达到循环生产与消费的目的,ArrayBlockingQueue用到了一个循环数组,而线程的阻塞与唤醒用到了JUC里面的reentrantlock和condition,condition就相当于wait/notify在juc包里面的一个实现

threadlocal及实现原理是什么

threadlocal是一种线程隔离机制,他提供了多线程环境下对于共享变量访问的一个安全性,一般我们解决共享变量的情况是加锁,保证同一个时刻对一个共享变量进行更新,并且基于happes-bofore规则的一个锁监视器的一个规则,又能保证对数据修改之后对其他线程是可见的.
但是加锁呢会导致性能上的一个下降,所以threadlocal利用了一个空间换时间的思想,也就是说每个线程里面都有一个容器,来存储共享变量的一个副本,然后每个线程只对自己的变量副本做更新操作
具体时间原理是,在thread类里面有一个成员变量叫threadlocalmap,他专门用来存储当前线程的共享变量的一个副本,后续这个线程对共享变量的一个操作呢,都是去从threadlocalmap里面去进行变更的

wait和notify为什么要在synchronized代码块中

wait让线程进入到阻塞状态
notify让阻塞的线程被唤醒
他们是成对出现的,从而去实现多个线程的通信

缓存雪崩和缓存穿透,缓存击穿的理解

缓存雪崩就是存储在缓存里面的大量数据在同一个时刻全部过期,原理缓存里面的全部流量请求到了数据库,从而导致数据库压力增加,造成数据库服务器崩溃的现象
造成原因
1.缓存中间件宕机
对缓存中间件做高可用集群来避免
2.缓存中里面大部分key都设置了相同的过期时间,导致同一时刻这些key都过期了
可以在失效时间里面增加1-5分钟的随机值
3.给缓存业务添加降级限流策略
4.给业务添加多级缓存

缓存穿透是短时间内有大量不存在的key打到数据上,造成数据库的压力增加
我认为这是一个攻击行为
1.把无效的key保存到redis里面并设置一个特殊值,比如像null字符串,缓存空对象
2.使用布隆过滤器来实现
把目标数据全部缓存到布隆过滤器,当攻击者用不存在的key来请求的时候,先在布隆过滤器里面去进行查询,如果不存在,就意味着这个key肯定在数据库里面也不存在,所以这个时候也不会去访问数据库
3.访问数据库的层面去加锁

缓存击穿是热点key突然过期,因为重建耗时长,在这段时间内大量请求落到数据库,带来巨大冲击
1.互斥锁
2.逻辑过期

volatile关键字有什么用

1.多线程下可以保证对共享变量的可见性
2.通过添加内存屏障防止多个指令之间的重排序

分布式锁的理解和实现

分布式锁是一种跨进程,跨机器节点的一种互斥锁
需要满足
排他性 只能有一个节点去访问共享资源
可重入性 他允许一个已经获得锁的线程在没有释放锁之前,重新去获得锁
锁的获取,释放
锁的失效机制 避免死锁的一个范围
比如redis 提供了setnx命令去实现锁的排他性,当key不存在的时候就返回1,存在就会返回0
然后还可以使用expire命令去设置锁的失效时间,从而去避免死锁.
redisson提供了分布式锁的封装实现,内置了一个watch dog机制,来对key做续期

死锁发生的原因和避免

死锁就是两个或者两个以上的线程,在执行的过程中去争夺同样一个共享资源,造成的相互等待的一个现象,如果没有外部的干预呢,线程会一直阻塞,无法往下去执行.这样一直处于相互等待资源的线程,我们称为死锁线程,
4个条件
1.互斥条件 共享资源X和Y只能被一个线程占用
2.请求和保持条件 线程T1已经取得共享资源X,在等待共享资源Y的时候,不释放共享资源X
3.不可抢占条件,其他线程不能强行抢占线程T1占有的资源
4.循环等待条件,线程T1等待线程T2占有的资源,线程T2等待线程T1占有的资源

比如我们可以一次性申请所有的资源,这样的话就不存在锁要等待了
占用部分资源,在进一步申请其他资源的时候,如果申请不到,我们可以主动去释放他占用的资源
按序申请,先去申请资源序号小的,再去申请资源序号大的,

springBoot自动装配机制的原理

就是自动把第三方组件的bean装载到IOC容器里面,不需要开发人员自动写bean相关的一个配置
在spring应用类里面加上@springBootApplication注解就可以去实现自动装配
而真正实现自动装配的注解是@EnableAutoConfiguration
@EnableAutoConfiguration:启用 SpringBoot 的自动配置机制

@Configuration:允许在上下文中注册额外的 bean 或导入其他配置类

@ComponentScan: 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。如下图所示,容器中将排除TypeExcludeFilter和AutoConfigurationExcludeFilter。
在这里插入图片描述
@EnableAutoConfiguration 是实现自动装配的重要注解,我们以这个注解入手。
@EnableAutoConfiguration:实现自动装配的核心注解
EnableAutoConfiguration 只是一个简单地注解,自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector类。
可以看出,AutoConfigurationImportSelector 类实现了 ImportSelector接口,也就实现了这个接口中的 selectImports方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中。
主要依靠三个关键的核心技术
1.引入starter,启动依赖组件的时候,要包含一个@configuration配置类,而在这个配置类里面,我们需要通过@Bean这个注解去声明需要装配到IOC容器里面的bean对象
2.这个配置类是放在第三方的jar包里面,然后通过springBoot中约定由于配置的这样一个理念,去吧这个配置类的全路径放在classpath:/meta-inf/spring.factories文件里面,这样springboot就能知道第三方jar配置类文件的位置,这个是用到了spring里面的springfactoriesLoader来完成的
3.springboot拿到第三方jar包里面声明的配置类以后,在通过spring提供的importselector这样一个接口来实现对这些配置类的动态加载,从而去完成自动装配这样一个动作
自动配置类其实就是通过@Conditional按需加载的配置类,想要其生效必须引入spring-boot-starter-xxx包实现起步依赖

redis和mysql如何保证数据一致性

当业务层去尝试读取某个数据的时候会尝试去redis里面去加载,如果命中就直接返回,如果没有命中,就直接从数据库里面查询,查询到数据后在把数据缓存到redis里面
1.先更新数据库,在更新缓存
2.先删除缓存,在更新数据库(高并发下用延迟双删 线程A先成功删除redis缓存,线程A再更新mysql,暂停2秒钟,其他业务逻辑导致耗时延迟 在执行一次删除)
3.先更新数据库,在删除缓存
在多线程条件下可能仍然会有这样的问题,可以基于rocketMq的可靠性消息通信
失败的请求写入MQ事务消息,异步重试,确保成功
也可以通过canal组件监控mysql中的binlog的日志,把更新后的数据同步到redis里面

CAS机制

他是unsafe类的一个方法,他的全程是compareandswap 比较并交换
主要是为了保证多线程下,共享变量一个原子性
主要是atomic报里面的原子实现
第二个是实现多线程对共享资源竞争的互斥性质

concurrentHashMap实现原理

1.concurrentHashMap整体架构
数组+链表+红黑色
默认会初始化一个长度等于16的数组
发生了哈希冲突会生成一条链表
当数组长度大于64并且链表长度大于等于8的时候
单向链表就会转化成红黑树,链表的长度小于6的时候,红黑树会退化成单向链表
2.concurrentHashMap的基本功能
在hashMap上提供了一个并发安全的实现
主要是通过对于node节点去加锁
3.concurrentHashMap在性能方面的优化
concurrentHashMap的锁的粒度是数组中的某一个节点,1.7是用的segment,锁的范围要更大,所以性能上他会更低.
引入红黑树这样的一个机制去降低了数据查询的时间复杂度.
数组的扩容,concurrentHashMap引入了多线程并发扩容的实现,多个线程对原始数据分片,每个线程去负责一个分片的数据迁移.
从而提升了扩容过程中数据迁移的一个效率

concurrentHashMap的size()方法来获取元素的个数,而在多线程并发环境中保证原子性的条件下去实现元素个数的累加,性能是非常低的
concurrentHashMap做了2个优化
concurrentHashMap在线程竞争不激烈的时候直接采用了CAS的方式,来实现原子性的递增
如果线程竞争比较激烈的情况下,直接使用一个数组来维护元素个数,如果要增加元素个数直接从数组中随机取一个,在通过CAS算法来实现原子递增,核心思想是引入数组来实现对并发更新的一个负载

hashMap如何解决哈希冲突

哈希冲突是由于哈希算法被计算的数据是无限的,而计算后的结果的范围是有限的,所以总会存在不同的数据,经过计算之后得到的值是一样,这个情况下就会出现所谓的哈希冲突
解决哈希冲突的方法有4种
1.开放定址法 从冲突位置的那个位置开始,从hash表中去找到一个空闲的位置,然后把发生冲突的元素存入到这个位置 threadlocal就用到了
2.链式寻址法 把存在hash冲突的key以单向链表的方式进行存储 hashMap用到了此方法
3.在hash法,再次用hash函数进行运算
4.建立公共溢出区 把hash表分为基本表和溢出表两个部分 凡是存在冲突的元素放在溢出表中

线程池如何知道一个线程的任务已经执行完成

引入contDownLatch这样一个计数器
它可以通过初始化指定的一个计数器去进行倒计时,其中他提供了两个方法,分别是await()阻塞线程,以及countDown()去进行倒计时,一旦倒计时归0,所有被阻塞在await()方法的线程都会被释放.

lock和synchronized的区别

1.lock和synchronized都是java中解决线程安全的一个工具
2.synchronized是java中的同步关键字
lock是JUC包里面提供的一个接口
synchronized 可以修饰在方法上面,修饰在代码块上,锁的对象是静态对象或者是类对象,就属于全局锁.如果是普通实例对象,锁的范围取决于实例的生命周期.
lock中锁的粒度是他提供的lock方法和unlock方法来决定的,
lock比synchronized灵活度更高,可以自主的决定上面时候加锁和释放锁,只需要调用lock和unlock这两个方法就可以了
同时lock还提供了非阻塞竞争锁的方法trylock(),这个方法可以通过返回true/false来告诉当前线程是否有其他线程正在使用锁了.
lock提供了公平和非公平锁的机制
公平锁是指线程竞争锁资源的时候,如果已经有其他线程正在排队,或者等待锁释放,那么当前竞争锁的线程是无法去插队的,而非公平锁不管是否有线程在排队等待锁,他都会去尝试去竞争一次锁.synchronized只提供了非公平的实现
3.lock是自旋锁实现性能优化 两者性能都差不多

seata 的理解

在微服务架构中,由于数据库和应用服务的拆分,导致原本一个事务单元中的多个DML操作,编程了跨进程或者跨数据库的多个事务单元的多个DML操作
封装了四种分布式事务模型
1.AT模式
是一种基于本地事务+二阶段协议来实现的最终数据一致性方案 也是默认解决方案
2.TCC模型
try confirm cancel
3.saga模式
4.XA模式

谈谈你对AQS的理解

AQS是多线程同步器,他是juc包中多个组件的底层实现,比如lock,countdownlatch,semaphre都用到了AQS,AQS提供了排它锁和共享锁,
排它锁就是多个线程去竞争同一共享资源的时候,同一时刻只允许一个线程去访问这样一个共享资源,多个线程中只有一个线程去获得这样一个锁的资源
比如lock中的reentrantlock重入锁的实现就是用到了AQS中的排它锁的功能
共享锁也称为读锁,同一时刻允许多个线程同时获得这样一个锁的资源,比如countdownlatch,semaphre都用到了AQS中的共享锁的功能
AQS用一个int 类型的互斥变量state用来记录锁竞争的一个状态,.0表示当前没有任何线程竞争锁资源,而大于等于1表示已经有线程正在持有锁资源
一个线程获得锁资源首先判断state是否等于0,表示无锁状态,.如果是就更新成1,表示占用到锁
在更新的时候用了CAS极致保证了一个更新的原子性
未获得锁的线程通过unsafe类的park方法去进行阻塞,把阻塞的线程按照先进先出的原则,去加入到双向链表的一个结构中,当获得锁资源的线程在释放锁之后,会从双向链表的头部去唤醒下一个等待的线程,再去竞争锁
公平锁是在竞争锁资源的时候,去判断双向链表是否有阻塞的线程,如果有就需要去排队等待
非公平锁是不管双向链表中是否存在,等待竞争锁的线程,那么他都会直接去尝试更改互斥变量中的state去竞争锁

如何保证mysql数据库的高可用

1.搭建mysql的主从集群
所有读请求都到mysqlA上 写请求都到mysqlB上
可以sharding-jdbc mycat等等一系列的产品
2.主从数据同步
当写请求落到主库的时候会产生一个data changes 并写入到bin log的日志文件中,有了bin log就只需要把bin log传输到指定的从库,然后从库根据bin log文件的内容解析并执行binlog中的sql语句,就可以完成数据的同步

mysql性能优化

1.硬件和操作系统层面的优化
cpu 可用内存大小 磁盘读写速度 网络带宽
一般是由DBA或者运维工程师去做
2.架构设计层面的优化
搭建mysql主从集群
读写分离设计
引入分库分表的机制
针对热点数据可以用redis mongoDB等菲关系型数据库
3.mysql程序配置优化
配置my.conf 默认连接数 bin.log 日志 缓存池bufferpool
4.sql执行优化
1.慢SQL的定位和排查可以通过慢查询日志
2.使用关键字explain 重点关注type key rows filterd字段 来关注sql执行慢的根本原因
3.使用show profile工具 是来分析sql语句资源消耗的工具
4.进行分页处理

spring bean的生命周期

1.创建前准备
bean在加载之前,要从上下文和一些配置中,去解析并且查找bean有关的扩展实现,比如像init-method容器在初始化bean的时候,会调用方法destroy method 容器在销毁bean的时候会调用的一些方法 以及beanfactorypostprocessor这一系列的bean前置和后置的一些处理扩展实现
2.创建实例阶段
通过反射去创建bean的实例对象,并且会扫描和解析bean声明的一些属性
3.依赖注入阶段
如果被实例化的bean,存在依赖其他bean对象的一些情况,则需要对这些依赖的bean进行对象注入,比如常见的@autowired 以及setter注入的这样的一些配置形式
4.容器缓存阶段
把bean保存到容器以及spring的缓存中,这个阶段的bean就可以被开发者去使用了
5.销毁实例阶段
应用上下文被关闭的时候,那么应用上下文的bean会被销毁,如果存在bean实现了像disposablebean接口,或者配置了destory-method属性的一些方法会在这个阶段会被调用

什么是消息队列

主要是由三个部分组成
生产者+broker代理+消费者
使用场景
1.异步处理
用在实施性不严格的一些场景
比如用户注册发送验证码,下单通知发送优惠券等等
服务方只需要把协调好的消息发送到消息队列,剩下的是由消费者的消息服务去处理,不需要等待消费者返回结果,就可以直接返回给客户端
2.应用解耦
把一些耦合度不高的系统关联起来
比如说是订单系统和优惠券积分系统有关联度有关联度,但是没那么紧密,每个系统只需要把约定的消息发送到MQ,另外的系统直接去消费这个消息就行了
3.流量削峰
一般是用在大流量入口的一些短时间业务,需求处理不完的一些服务,为了去权衡高可用把大量的一些并行任务发送到MQ,根据MQ和存储和分发的功能的平稳的去处理后续的一些业务,起到了一个大流量缓存的一个作用

什么是MVCC

可以从三种并发场景来说
1.读和读,两个线程A和B同时进行读操作,不会产生任何的并发问题
2.读写并发,两个线程A和B分别进行读写操作,会出现脏读,幻读,不可重复读的问题
3.写和写的并发,两个线程A和B同时进行写操作,这种问题下可能会存在数据更新丢失问题.
而MVCC多版本并发控制,也是为了解决事务并发中并发安全问题的无锁并发控制技术.
他是通过数据库记录的隐式字段undo日志和read view来实现的
MVCC主要解决三个问题
1.读写并发阻塞问题
2.MVCC采用乐观锁的实现,降低了死锁的概率
3.解决了一致性读的问题

spring如何解决循环依赖问题

spring去设计了三级缓存去解决循环依赖的问题
当我们去通过getBean()去获取对象实例的时候,spring会先从一级缓存去找,如果发现一级缓存没有找到,就去二级缓存去找,如果一二级都没找到,就意味着这个bean没有被实例化,spring容器会去实例化这个bean,加入一个标记表示他是否存在循环依赖,如果不存在放入二级缓存,否则标记这个bean存在循环依赖,等待下一次轮巡的时候去复制,也就是解析@autowired注解,等@autowired注解复制完之后放入一级缓存.
一级缓存存放所有成熟的bean
二级缓存存放所有早起的bean
三级缓存存放代理bean,当调用getbean()方法的时候,需要通过目标bean需要通过代理工厂来创建,这个时候会把创建好的实例保存在三级缓存,最终也会把复制好的bean同步的一级缓存.

spring哪些情况下不能解决循环依赖

1.多实例bean,通过setter注入
2.构造器注入的bean
3.单例的代理bean,通过setter注入
4.设置@dependsOn 注解的bean

mybatis #{}与${}的区别

首先#,$两种占位符都是实现动态SQL的一种方式,通过这两种方式可以把参数传到XML里面,#占位符等同于jdbc里面一个?号占位符,相当于向preparedStatemet里面的预处理语句设置参数,在设置参数的时候,如果有特殊字符会自动进行转义,所以#占位符可以防止SQL注入,$传参相当于直接把参数拼接到了原始SQL里面,mysbatis不会对它进行任何的特殊处理,$和#最大区别是,$是动态参数,#是占位符,动态参数无法防止SQL注入的一个问题,所以在实际应用里面尽可能的去使用#占位符,$的传参可以应用在一些动态SQL的场景里面,比如动态传递表名或者动态设置排序字段等等.

Spring 事务失效

  1. 抛出检查异常导致事务不能正确回滚
  • 原因:Spring 默认只会回滚非检查异常
  • 解法:配置 rollbackFor 属性
    • @Transactional(rollbackFor = Exception.class)
  1. 业务方法内自己 try-catch 异常导致事务不能正确回滚
  • 原因:事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉
  • 解法1:异常原样抛出
    • 在 catch 块添加 throw new RuntimeException(e);
  • 解法2:手动设置 TransactionStatus.setRollbackOnly()
    • 在 catch 块添加 TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
  1. aop 切面顺序导致导致事务不能正确回滚
  • 原因:事务切面优先级最低,但如果自定义的切面优先级和他一样,则还是自定义切面在内层,这时若自定义切面没有正确抛出异常…
  • 解法1、2:同情况2 中的解法:1、2
  • 解法3:调整切面顺序,在 MyAspect 上添加 @Order(Ordered.LOWEST_PRECEDENCE - 1) (不推荐)
  1. 非 public 方法导致的事务失效
  • 原因:Spring 为方法创建代理、添加事务通知、前提条件都是该方法是 public 的
  • 解法1:改为 public 方法
  • 解法2:添加 bean 配置如下(不推荐)
    @Bean
    public TransactionAttributeSource transactionAttributeSource() {
    return new AnnotationTransactionAttributeSource(false);
    }
  1. 父子容器导致的事务失效
    现在配置了父子容器,WebConfig 对应子容器,AppConfig 对应父容器,发现事务依然失效
  • 原因:子容器扫描范围过大,把未加事务配置的 service 扫描进来
  • 解法1:各扫描各的,不要图简便
  • 解法2:不要用父子容器,所有 bean 放在同一容器
  1. 调用本类方法导致传播行为失效
  • 原因:本类方法调用不经过代理,因此无法增强
  • 解法1:依赖注入自己(代理)来调用
  • 解法2:通过 AopContext 拿到代理对象,来调用
  • 解法3:通过 CTW,LTW 实现功能增强
  1. @Transactional 没有保证原子行为
  • 解法1:synchronized 范围应扩大至代理方法调用
  • 解法2:使用 select … for update 替换 select

redis数据类型应用

是否点赞用ZSet
获取点赞的排行榜,关注用户用Set,共同关注用set的交集
登录信息放在hash
签到功能 BitMap
获取坐标点 从而计算距离 redis5以后的新数据类型 Geo
HyperLogLog用于大量数据统计插入(重复不插入)

最重要的 JVM 参数总结

-Xms 最小堆大小
-Xmx 最大堆大小
-Xmn 新生代分配内存

JDK 动态代理和 CGLIB 动态代理对比

JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。

MyISAM 和 InnoDB 的区别

1.是否支持行级锁
2.是否支持事务
3.是否支持外键
4.是否支持数据库异常崩溃后的安全恢复
5.是否支持 MVCC

mysql哪些情况下索引会失效

  1. 避免数据类型的隐式转换
    select name,phone from customer where id = ‘111’;
    避免使用双%号的查询条件。如:a like ‘%123%’,(如果无前置%,只有后置%,是可以用到列上的索引的)
    使用 left join 或 not exists 来优化 not in 操作,因为 not in 也通常会使用索引失效
    禁止使用 SELECT * 必须使用 SELECT <字段列表> 查询
    in 操作可以更有效的利用索引,or 大多数情况下很少能利用到索引
    在这里插入图片描述
    WHERE 从句中禁止对列进行函数转换和计算

什么字段要创建索引

不为 NULL 的字段
被频繁查询的字段
被作为条件查询的字段
频繁需要排序的字段
被经常频繁用于连接的字段

SpringMVC 工作原理了解吗?

客户端(浏览器)发送请求,直接请求到 DispatcherServlet。
DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。
解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由 HandlerAdapter 适配器处理。
HandlerAdapter 会根据 Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑。
处理器处理完业务后,会返回一个 ModelAndView 对象,Model 是返回的数据对象,View 是个逻辑上的 View。
ViewResolver 会根据逻辑 View 查找实际的 View。
DispaterServlet 把返回的 Model 传给 View(视图渲染)。
把 View 返回给请求者(浏览器)

MyBatis 是如何进行分页的?分页插件的原理是什么?

答:(1) MyBatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页;(2) 可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能,(3) 也可以使用分页插件来完成物理分页。

分页插件的基本原理是使用 MyBatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。

举例: select _ from student ,拦截 sql 后重写为: select t._ from (select * from student)t limit 0,10

你给我介绍一下java里面的设计模式

在这里插入图片描述
Spring 中都使用了哪些设计模式?

代理模式:在 AOP 中有使用

单例模式:Bean 默认是单例模式

模板方法模式:JdbcTemplate

工厂模式:BeanFactory

观察者模式:Spring 事件驱动模型就是观察者模式很经典的一个应用,比如,ContextStartedEvent 就是 ApplicationContext 启动后触发的事件

适配器模式:Spring MVC 中也是用到了适配器模式适配 Controller

谈谈redis过期策略

redis 过期策略是定期删除+惰性删除

定时删除:在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。(创建定时器删除)
惰性删除:放任键的过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。(使用的时候删除)
定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面过期的键。至于要删除多少过期键,以及要检查多少个数据库,则有算法决定。(定期扫描删除)

在Redis2.8版本后,可以通过修改配置文件redis.conf 的 hz 选项来调整这个次数。
建议不要将这个值设置超过 100,否则会对CPU造成比较大的压力。

redis内存淘汰策略

在配置文件redis.conf 中,可以通过参数 maxmemory 来设定最大内存:
1)volatile-lru 利用LRU算法移除设置过过期时间的key (LRU:最近使用 Least Recently Used ) 。

2)allkeys-lru 利用LRU算法移除任何key (和上一个相比,删除的key包括设置过期时间和不设置过期时间的)。通常使用该方式。

3)volatile-random 移除设置过过期时间的随机key 。

4)allkeys-random 无差别的随机移除。

5)volatile-ttl 移除即将过期的key(minor TTL)

6)noeviction 不移除任何key,只是返回一个写错误 ,默认选项,一般不会选用。

在redis.conf 配置文件中,可以设置淘汰方式:

多线程里面常用的锁

公平锁和非公平锁
公平锁:是指多个线程按照申请锁的顺序来获取锁

非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获锁。

非公平锁一上来就尝试占用锁,如果尝试占用失败,就采用公平锁的方式到末尾排队。

在高并发的情况下,有可能造成优先级反转或饥饿现象

非公平锁的优点在于吞吐量比公平锁大。

ReentrantLock:可以指定构造方法的boolean类型来指定是公平锁还是非公平锁,默认是非公平锁

synchronized:是一种非公平锁

可重入锁(又名递归锁)
可重入锁:指的是同一线程外层方法获得锁之后,内层递归方法仍然能够获得该锁的代码

在同一个线程在外层方法获取锁的时候,在进入内层方法的时候会自动获取锁

也就是说,线程可以进入任何一个它自己已经拥有的锁所同步着的代码块

可重入锁的最大作用是避免死锁

ReentrantLock和synchronized就是一个典型的可重入锁
自旋锁
是指尝试获取锁的线程不会立即被阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线上下文切换的消耗,缺点是循环会消耗CPU
独占锁
是指该锁一次只能被一个线程所持有。

ReentrantLock和synchronized都是独占锁

共享锁
是指该锁可以被多个线程所持有

ReentrantReadWriteLock:其读锁是共享锁,其写锁是独占锁

读锁的共享锁可保证并发读是非常高效的,读写、写读、写写的过程是互斥的

过滤器拦截器aop执行顺序

监听器 => 过滤器=> 拦截器=> AOP。
在这里插入图片描述

Gateway的理解

在这里插入图片描述

Predicate 就是为了实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理
通过时间匹配
通过 Cookie 匹配
通过 Host 匹配
通过请求方式匹配
通过请求路径匹配
通过请求参数匹配
通过请求 ip 地址进行匹配

配置统一跨域 配置负载均衡

Spring Cloud OpenFeign的原理

@EnableFeignClients
@Import(FeignClientsRegistrar.class)这个FeignClientsRegistrar跟Bean的动态装载有关

点击进去有个registerBeanDefinitions方法通过名称可以知道是一个Bean的注入方法

registerDefaultConfifiguration 方法内部从 SpringBoot 启动类上检查是否有@EnableFeignClients, 有该注解的话, 则完成 Feign 框架相关的一些配置内容注册

registerFeignClients 方法内部从 classpath 中, 扫描获得 @FeignClient 修饰的类, 将类的内容解析为 BeanDefifinition , 最终通过调用 Spring 框架中的BeanDefifinitionReaderUtils.resgisterBeanDefifinition 将解析处理过的 FeignClientBeanDeififinition 添加到 spring 容器中.

redis5跟redis6的区别

  1. 多线程的读写IO, 但是最终执行用户命令的线程依然是单线程的
    2、客户端缓存Client Side Cache
  2. ACL细粒度安全控制acces control list
    ACL 是对于命令的访问和执行权限的控制,默认情况下,可以有执行任意的指令,兼容以前版

    4、增加SSL模块
    5、增加RESP3新的通信协议

java11的新特性

java11:
HTTP Client 标准化
String 增强
Optional 增强
ZGC(可伸缩低延迟垃圾收集器)
Lambda 参数的局部变量语法
启动单文件源代码程序

手动分页实现思路

比如有些大批量的插入就需要使用到使用手动分页

/**
     * 手动分页类
     * @param datas
     * @param pageSize
     * @param pageNo
     * @param <T>
     * @return
     */
    public static <T> List<T> getPageSizeDataForRelations(List<T> datas,int pageSize,int pageNo){
        int startNum = (pageNo-1)* pageSize+1 ;                     //起始截取数据位置
        if(startNum > datas.size()){
            return null;
        }
        List<T> res = new ArrayList<>();
        int rum = datas.size() - startNum;
        if(rum < 0){
            return null;
        }
        if(rum == 0){                                               //说明正好是最后一个了
            int index = datas.size() -1;
            res.add(datas.get(index));
            return res;
        }
        if(rum / pageSize >= 1){                                    //剩下的数据还够1页,返回整页的数据
            for(int i=startNum;i<startNum + pageSize;i++){          //截取从startNum开始的数据
                res.add(datas.get(i-1));
            }
            return res;
        }else if((rum / pageSize == 0) && rum > 0){                 //不够一页,直接返回剩下数据
            for(int j = startNum ;j<=datas.size();j++){
                res.add(datas.get(j-1));
            }
            return res;
        }else{
            return null;
        }
    }

synchronized锁升级过程

synchronized 锁升级过程就是其优化的核心:偏向锁 -> 轻量级锁 -> 重量级锁
偏向锁、轻量级锁、重量级锁适用于不同的并发场景:

偏向锁:无实际竞争,且将来只有一个申请锁的线程会使用锁。有其他线程来竞争,就撤销偏向锁;
轻量级锁:无实际竞争,多个线程交替使用锁;允许短时间的锁竞争。不会造成阻塞,存在竞争,转为重量级锁
重量级锁:有实际竞争,且锁竞争时间长。会阻塞

偏向锁和CAS的区别

一、偏向锁:
​ 持有偏向锁的线程第一次进入的时候需要加锁,当第二次进入的时候不需要加锁,类似于看门大爷偷偷为你走后面,第一次来的时候需要通行证,之后就不需要通行证一样放你进来。

​ 偏向锁运行的时候如果遇到抢占锁(优先级更高的锁),则偏向锁会被挂起。

二、CAS(comparent and swap)自旋锁:
​ Lock锁,属于CAS自旋锁,原理简化理解为在一定时效内死循环对比,如果超时获取不了,就挂起(进入阻塞状态),进入重量级锁,等待线程唤醒自己,因为没涉及到锁机制,也没涉及到操作系统干涉线程,可以在某种程度上避免cpu的上下文切换,从而相对于阻塞锁来讲性能损耗更小

​ 优点:不再加锁,不再阻塞线程,通过CAS算法进行循环对比,提高程序的运行性能

​ 但是如果CAS自旋锁过于多,竞争过于激烈,性能会下降

cas是怎么保证原子性的

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该 位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前 值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”

通常将 CAS 用于同步的方式是从地址 V 读取值 A,执行多步计算来获得新 值 B,然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改,则 CAS 操作成功。

ABA问题解决

将类变成原子类 在juc包中有默认的原子类型原子操作类
操作过程添加版本号 AtomicReference

解释一下锁降级

锁降级指的是写锁降级成为读锁。如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这种分段完成的过程不能称之为锁降级。锁降级是指把持住(当前拥有的)写锁,再获取到读锁,随后释放(先前拥有的)写锁的过程。

如何设置线程池的参数

需要根据几个值来决定

tasks :每秒的任务数,假设为500~1000

taskcost:每个任务花费时间,假设为0.1s

responsetime:系统允许容忍的最大响应时间,假设为1s

做几个计算

corePoolSize = 每秒需要多少个线程处理?

threadcount = tasks/(1/taskcost) = tasks*taskcout = (500 ~ 1000)*0.1 = 50~100 个线程。

corePoolSize设置应该大于50。

根据8020原则,如果80%的每秒任务数小于800,那么corePoolSize设置为80即可。

queueCapacity = (coreSizePool/taskcost)*responsetime

计算可得 queueCapacity = 80/0.1*1 = 800。意思是队列里的线程可以等待1s,超过了的需要新开线程来执行。

切记不能设置为Integer.MAX_VALUE,这样队列会很大,线程数只会保持在corePoolSize大小,当任务陡增时,不能新开线程来执行,响应时间会随之陡增。

maxPoolSize 最大线程数在生产环境上我们往往设置成corePoolSize一样,这样可以减少在处理过程中创建线程的开销。

rejectedExecutionHandler:根据具体情况来决定,任务不重要可丢弃,任务重要则要利用一些缓冲机制来处理。

keepAliveTime和allowCoreThreadTimeout采用默认通常能满足。

以上都是理想值,实际情况下要根据机器性能来决定。如果在未达到最大线程数的情况机器cpu load已经满了,则需要通过升级硬件和优化代码,降低taskcost来处理。

spring初始和销毁使用哪些注解

使用 spring 提供的 @PostConstruct 和 @PreDestroy 注解来实现对象实例的启动和销毁时要执行的代码。

Spring是怎么将AOP应用到Bean的生命周期中的

initializeBean这个方法,它主要干了这么几件事

执行Aware接口中的方法
执行生命周期回调方法
完成AOP代理

@EnableAspectJAutoProxy注解实际上就是向容器中注册了一个AnnotationAwareAspectJAutoProxyCreator,这个类本身就是一个后置处理器,AOP代理就是由它在这一步完成的。

JVM 由哪些部分组成?

JVM 的结构基本上由 4 部分组成:

类加载器,在 JVM 启动时或者类运行时将需要的 class 加载到 JVM 中

执行引擎,执行引擎的任务是负责执行 class 文件中包含的字节码指令,相当于实际机器上的 CPU

内存区,将内存划分成若干个区以模拟实际机器上的存储、记录和调度功能模块,如实际机器上的各种功能的寄存器或者 PC 指针的记录器等

本地方法调用,调用 C 或 C++ 实现的本地方法的代码返回结果

JVM如何判断哪些对象可以回收

引用计数算法和可达性分析算法

JVM内存模型中对象的组成结构

在这里插入图片描述

mybatis分页插件原理

Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。

使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。

springboot设置文件上传大小

springboot默认文件大小的限制是1MB,超过1MB会出现这个错误:org.springframework.web.multipart.MultipartException。

nacos 如何防止多节点读写并发冲突

Nacos使用的是CopyOnWrite思想防止并发冲突。

Nacos: provider向注册中心注册,将服务器相关信息写入内存。 consumer拉取,读取注册中心服务信息。

@RefreshScope 配置不生效问题

可能是nacos版本问题
在这里插入图片描述
在这里插入图片描述

nacos集群如何搭建

初始化 nacos 必须的数据库表并配置
nacos 集群配置

复制 cluster.conf 文件 修改 cluster.conf 文件
在这里插入图片描述

nacos集群选举怎么选的

Nacos集群采用raft算法来实现,它是相对zookeeper的选举算法较为简单的一种。

redis集群搭建

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Redis集群选举机制

当slave发现自己的master变为FAIL状态时,便尝试发起选举,以期成为新的master。由于挂掉的master可能会有多个slave,从而存在多个slave竞争成为master节点的过程

RabbitMQ如何保证有序消费消息?如何防止消息被重复消费?

在这里插入图片描述
在这里插入图片描述
如何防止消息被重复消费

我们引入“幂等性”这一概念。幂等性指的是同一操作执行多次,结果不改变,就算重复消费也没问题,因为重复的只会做一次。

1.比如,你拿到这个消息做数据库的insert操作。那就容易了,给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。

2.再比如,你拿到这个消息做redis的set的操作,那就容易了,不用解决,因为你无论set几次结果都是一样的,set操作本来就算幂等操作。

3.如果上面两种情况还不行,上大招。准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。

INNODB如何保证事务的四个特性?

原子性、一致性、持久性通过数据库的redo log和undo log来完成。redo log称为重做日志,用来保证事务的原子性和持久性。undo log用来保证事务的原子性和一致性。
InnoDB的默认隔离级别REPEATABLE READ + Next-Key Locking,保证了数据库的隔离性,不出现并发一致性问题,且理论上效率高于SERIALIZABLE隔离级别。

mycat高可用方案

haproxy安装和配置
​在mycat的服务器上安装xinetd
​ keepalive安装

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

time丶sand

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值