Spring中的Bean是线程安全的嘛?
Spring线程中的bean的作用域主要分为单例bean,和多例两种
- singleton单例的bean在spring容器中之存在一个全局共享的实例
- prototype每次调用getBean的时候都会创建一个新的对象
所以这个问题要区分是多例,还是单例
这里如果是多例的那么毋庸置疑,每个线程用到的都是独立的一份所以是线程安全的。
如果是单例就需要考虑bean是否有加锁,如果没有那么就是非线程安全的。
解决方式呢,一般可以考虑使用Threadlocal来保证线程安全。
实现方式是有一个容器 叫做ThreadlocalMap 保存每个线程使用的bean
谈谈你对SpringBean的理解
- 什么是SpringBean,SpringBean是在Spring中构成应用主干并由Spring IOC管理的对象。
- 定义SpringBean有三种方式
- 更具XML的方式配置
- 基于注解扫描的方式配置
- @controller
- Service
- Repostory
- Component
- 基于java类的配置
- Bean的创建
- 如果是单例则需要首先清楚缓存
- 实例化bean,将BeanDefinition转换成BeanWrapper
- 如果存在工厂方法则使用工厂方法初始化。
- 一个类有多个构造行数,每个构造函数都有不同的参数,所以需要更具参数锁定构造函数并进行初始化。
- 如果既不存在工厂方法也不存在带有参数的构造函数,则默认调用默认的构造函数进行bean的实例化。
- MergedBeanDefinitionPostProcessor的应用
- 依赖处理
- 属性填充
- 循环依赖检查
- 之前说过Spring中解决循环依赖只对单例有效,对于prototype的bean是没有好的解决办法,这里只是判断是否有循环依赖,并判断是否需要抛出异常。
- 注册DisposableBean
- 完成创建兵器返回
链接: bean创建.
什么是死锁
一组互相竞争资源的线程,相互等待对方资源,而造成的情景,就是死锁。
发生死锁的原因
- 互斥条件
- 占有且等待
- 线程T1获取了资源1,尝试获取资源2不释放资源1
- 不可抢占
- 循环等待
如何避免死锁
就是破坏发生死锁的4种原因
- 互斥条件不可破坏因为是保证线程安全的基本条件
- 尝试一次获取所有资源
- 如果获取不到其他资源的情况下,可以先释放自己的资源,然后从新获取
- 依次获取,先获取小的之后在获取大的
谈谈Zookeeper的理解
集群管理
- 集群选选
- 临时节点+ZK监听机制
- 临时有序节点
- 分布式锁
- 集群管理
- 根据持久化节点+临时节点
链接: zk.
- 根据持久化节点+临时节点
谈谈你对ES的理解
- ES是建立在全文搜索引擎库Apache Lucene基础上的一个开源搜索和分析引擎
- 分布式存储检索速度快,所以经常用来做全文检索
- ES快
- 基于基于Lucene上开发的一个全文搜索引擎,Lucene会对数据进行分词保存
- ES采用了倒排索引,通过属性值来确定数据位置的索引,避秒了全表检索
- ES种存储数据采用了分片机制
- ES扩展性好,可以通过水平扩展增加服务器来提升性能
- ES提供了数据汇总和所以声明周期管理的一些功能
- ES使用时要避免的问题
- 避免使用复杂的查询
- 避免使用深度分页查询
- 因为分页支持form和size参数,查询的时候要构建from和size的优先队列,然后回传到网关节点,然后网关节点对这些优先队列进行排序,在找到正确的size文档,form足够大的时候会有oom和网络传输性差的一些问题
谈谈Docker的理解
- Docker是一个轻量级虚拟机,也是一个容器化的实现工具,使用的是GOlong语言进行开的,基于Linux内核的cgroup等一些技术对进程进行封装和隔离属于操作系统层面的虚拟化技术。它对不同的应用程序使用Docker的标准来实现构建
- Docker的优势
- 资源占用小,因为属于容器,不需要进行硬件虚拟化,也不需要运行完针的操作系统所带来的额外资源开销,所以资源利用率更高,无论是文件存储,还是数据存储逗比传统的虚拟机技术更高效,内存消耗也更小
- 启动速度更快,因为它是直接运行在宿主机的内核上,不需要启动完整的操作系统所以能做到秒级甚至毫秒级的启动时间。
- 迁移更轻松,Docker确保了执行环境的一致性,所以应用迁移更容易,无所是什么平台环境,可以参考JVM。
- 维护和扩展性更好,Docker使用的是分层存储和镜像技术让应用重复部分服用更佳容易,也让应用的维护更加简单,有官网镜像。
- 运行环境的一致性
- Docker的核心组件
- 镜像是一个特殊的文件系统,在这个文件系统里面包含可以运行Linke的程序以及相关的一些数据,一个镜像可以包含一个完整的操作系统环境,里面可以仅仅安装Apache或者用户锁需要的应用程序,也可以用来创建Docker容器
- 容器:是镜像运行的实体,Docker用容器去运行我们的应用,容器从镜像创建的运行实例,他可以被启动、开始、停止、删除,每个容器都是相互独立的
- 仓库,是集中存放镜像个一个地方
- 仓库注册服务器,是同一注册多个仓库的
实现分布式锁中的Redis,和ZK哪种更好
- 为什么要使用分布式锁:要解决在同一时刻多个线程,进程访问共享资源带来线程安全问题
- 而锁的用途可以分为两类
- 共享锁(读锁):允许多个线程或进程访问,多用于读
- 排他锁(独占锁,写锁):多用于写
- Redis提供两种方式来实现分布式锁
- 基于Redis本身提供的SET NX(SET key value NX PX milliseconds)
- 获取一把锁
- 失败了 自动重试
- 再次失败等待一定时间重试或者放弃
- 基于Redission的客户端来实现分布式锁,调用lock和uplock通过Lue脚本实现,支持了看门狗来完成一个锁的续期(加长持有锁的时间)
- 基于Redis本身提供的SET NX(SET key value NX PX milliseconds)
- zk实现分布式锁主要是通过临时节点和监听+ 同级目录下同名节点唯一来实现的。
负载均衡
Dubbo提供4中负载均衡默认是Random
- Random ,随机算法,可以对性能较好的服务器设置较大权重,权重越高,几率越大。
- Random RoBin 轮询,按照公约后的权重比例轮询。
- LeastActive,最少活跃调用书,处理越慢的服务器调用次数越少。
- ConsistentHash,一致性Hash。相同参数的请求总是发送给同一个服务器。
如何在不加锁的情况下解决线程安全问题
- Threadlocal
Spring如何解决循环依赖的问题
- 利用缓存机制来解决循环依赖的问题
- 创建bean的时候会先尝试在缓存中获取
- 获取失败创建bean实例然后把bean实例放到缓存中
- 但是在把实例放到缓存中的时候之前,会提前暴露一个getObeectFactory的方法
- 后续需要使用bean的其他bean是通过getObeectFactory来加载bean的
- Spring只能解决set的循环依赖,如果是构造函数的那么是不能解决的,只能抛出异常
链接: bean创建.
谈谈你对JVM的理解
- JVM是java代码的运行环境,其中包含JRE,和JDK,包含运行环境和变异环境。
- JVM包括内存模型,加载机制,回收机制,锁机制,等具体请查看我其他博文
分布式事务的解决方案
- 两段式提交
- 三段式提交
- X/OPEN模型
- CAP/BASE
- Setea
- TCC
- AR
- XA
- Saga
lock和synchronized的区别
共同点
- 都是JAVA为了解决线程安全的问题使用的
不同点
- lock是属于JUC包下的接口,其实现主要是重入锁(支持公平锁,默认是非公平锁,可以绑定多个条件)
- synchronized有两种方式来控制锁的力度
- 添加在方法层面
- 修饰在代码块上
- 可以通过加锁对象的生命周期来控制锁的作用范围
- 如果是静态对象或者是类对象,锁就属于全局锁
- 如果锁对象是普通实例对象,锁取决于实例的生命周期
- lock锁的例如是通过lock和unlock方法决定的
- lock比synchronized的灵活性更高
- lock提供非阻塞竞争锁的方法 trylock,返回true或false来告诉线程是否已经有线程占用锁
- synchronized锁的释放是被动的
- synchronized只提供了非公平锁的实现
- synchronized和lock的性能差别目前不大,因为synchronized已经优化了几代了
- synchronized引入了偏向锁,轻量级锁、重量锁、锁升级
- lock是用作自选锁的方式来实现的CAS
Mysql性能优化
- 硬件和操作系统层面的优化
主要影响有CPU、内存 - 架构设计层面的优化
- 搭建MySQL主从集群、主主集群
- 读写分离设计
- 引入分库分表的机制
- 通过分库可以降低单个服务器的IO压力
- 通过分表可以降低单表数据量,提高查询效率
- 针对热点数据可以引入redis等
- MySQL程序配置的优化
- my.cnf配置文件来设置配置5.7默认连结束151个,可以通过配置文件来修改
- beanLog日志默认是不开启的,可以设置开启
- 缓存吃Bufferpool默认大小配置
- SQL执行的优化
- 慢sql的定位和排查
- 可以通过慢查询日志和慢查询工具分析,得到有问题的sql列表
- 通过执行计划来分析,重点关注type、key、rows、filterd等字段定位执行慢的更笨原因
- 使用Show、Profile工具
- 慢sql的定位和排查
Spring AOP原理
- 创建代理对象
- 拦截母类
- 调用代理
谈谈你对AQS的理解
AQS是多线程同步器,他是JUC包中多个组件的底层实现,比如lock、Countdownlatch,
AQS提供两种锁的机制分别是排它锁和共享锁
- 排它锁,存在多个线程去竞争的资源,只有一个线程能使用资源,其他线程不能访问。
- 共享锁,同一时间可以有多个线程访问资源 一般用在只读文件上。
问题
- 互斥变量的设计和如何保证多线程同时更新复制变量的安全性
AQS通过int类型的静态变量state用来记录锁竞争的一个状态0表示没有竞争,大于等于1表示已经有线程正在持有锁,一个线程在获取锁资源的时候会判断 state是否等于0,如果是则更新成为1,代表占有锁,这个动作的工程中如果有多个线程同时进行动作,就会导致线程安全问题,因此AQS才用CAS机制来处理这个问题。 - 未竞争到锁资源的线程的等待以及竞争到资源的线程释放所后的唤醒
为获取到锁的线程会调用一个方法,来阻塞线程并存放到一个双向链表中,获取到锁的线程在执行完操作之后,会唤醒双向链表头部的线程。 - 锁竞争的公平性和非公平性
AQS在竞争锁的时候会先判断双向链表中是否有线程在等待,如果没有就是非公平竞争,如果有就是排队公平竞争。