线程池创建 ThreadPoolExecutor
核心线程数, 最大线程数, 空闲线程存活时间, 时间单位, 任务队列( 用来保存等待执行任务的队列 ) 线程工厂类( 创建线程设置优先级 )
饱和策略 ( 当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务 )
公平锁和非公平锁的区别
公平锁: 多个线程按照申请锁的顺序去获得锁,线程会直接进入队列排队 优点: 所有线程都能得到资源,不会饿死在队列中
缺点: 吞吐量下降很多,可能会造成其他线程阻塞
非公平锁: 多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入队列,如果获取到就直接获取锁
优点: 可以减少cpu唤醒线程的开销,
缺点: 可能会导致队列中间的线程一直获取不到锁或者长时间获取不到锁
线程锁
主要用来给方法,代码块加锁,当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或代码, 比如synchronized lock等
进程锁
为了控制桶一操作系统中多个子进程访问某个共享资源,因为进程具有独享性,各个进程无法访问其他进程的资源
分布式锁应该具备哪些条件
1.一个方法在同一时间只能被一个机器的一个线程执行
2.高可用高性能的获取与释放锁
3.具备锁失效机制,防止死锁
分布式锁三种实现方式
基于数据库实现分布式锁
基于缓存(redis)实现分布式锁
基于zookeeper实现分布式锁
分布式锁的特点
互斥性 任意时刻,只能有一个客户端获取锁,
安全性 锁只能被持有该锁的客户端删除,不能被其他客户端删除
死锁 获取锁的客户端因为某种原因而未释放锁
容错 当部分节点宕机 客户端仍能获取锁或释放锁
redis如何实现分布式锁
SETNX key value 此命令具有原子性,只有在key不存在情况下,才能set成功
1.加锁最简单的方法就是setnx命令,key是锁的唯一标识,按业务决定命名,
2.解锁,有加锁就有解锁,释放锁的最简单方法就是执行del指令,
synchronized和lock的区别
sunchronized是jvm层面实现的,java提供的关键字
lock是api层面的锁
synchronized不需要手动释放锁,lock需要手动释放锁
sunchronized锁适合代码少量的同步问题 lock锁适合大量代码同步问题
hashMap底层原理
底层: 分为jdk1.7和jdk1.8, 1.7之前是数组+链表,使用的是头插法 1.8之后是数组+链表或红黑树采用的是尾插法 当单链表大于等于8并且hash桶大于等于64的时候, 单链表会转换成红黑树 在红黑树中的节点小于等于6的时候它会重新转换成单链表
扩容机制 : hashMap初始长度默认是16负载因子是0.75 它的扩容机制是使用懒扩容 就是说在首次调用 put 的时候才会进行判断然后扩容,
1.将数组长度扩容为原来的2倍
2.将原来数组中的元素进行重新放到新的数组中
什么时候进行扩容
1. 在首次调用put方法的扩容
2 . 当HashMap中的元素个数超过数组大小 (数组长度*负载因子)就会进行扩容因为数组默认大小是16也就是说当HashMap中的元素个数超过16*0.75=12的时候就把数组的大小扩展到2*16=32然后重新计算每个元素在数组的位置,
3. 当HashMap中的其中一个链表的对象个数如果达到了8个,此时如果数组长度没有达到64, 那么HashMap会先扩容解决,如果已经达到了64那么这个链表会变成红黑树, 节点类型Node变成TreeNode类型 如果映射关系被移除后,下次执行resize方法的时候判断树的节点个数低于6个,也会再把树转为链表
HashMap是线程不安全的
为什么HashMap不是线程安全的
HashMap在并发执行put操作时会引起死循环,导致cpu利用率100%
因为多线程会导致HashMap的Node链表形成环形数据结构,一旦形成环形数据结构,Node的next节点永远不为空,就会在获取Node时产成死循环
如何让HashMap的线程安全
1.替换成Hahtable, Hashtable通过对整个表上锁实现线程安全,因此效率低
2.使用synchronizeMap方法包装一下比如
public staticMapsynchronizeMap(MapM)返回指定映射支持的同步
3. 使用ConcurentHashMap 它使用分段锁来保证线程安全
加载因子为啥是0.75呢(泊松分布)
1. 加载因子过高 ,虽然减少了开销提高了空间利用率,但同时也增加了时间成本
2. 加载因子过低 ,虽然减少了查询时间成本,但是空间利用率很低,同时提高了rehash操作的次数,
选择0.75作为默认的加载因子,完全是时间和空间成本上寻求的一种折中选择
hashmap和hashtable的区别
底层数据结构不同 hashMap1.7之前是数组+链表1.8之后加入了红黑树 HashTable是数组+链表
扩容机制不同 HashMap扩容规则是当前容量翻倍,Hasntable扩容规则为当前容量翻倍+1
实现方式不同 HashMap继承的是AbstractMap类,Hashtable继承的是Dictionary类
初始容量 HashMap初始容量的16, Hashtable初始容量是11
添加key-value的hash值算法不同 HashMap添加元素是使用自定义的哈希算法, 而Hashtable是直接采用key的hashCode()
Hashtable的健或值,不支持null, Hashmap的健值则支持null
重写equals方法为啥要重写hashcode方法
1.使用hashcode方法提取校验,避免每一次比对都调用equals方法,提高效率
2.保证是同一个对象,如果重写了equals方法而没有重写hashcode方法会出现equals相等hashcode不相等的情况
hashcode方法就是为了避免这种情况的出现
深拷贝和浅拷贝的区别
浅拷贝是指向被复制的内存地址,而深拷贝是创建新的内存地址用于存放复制的对象
浅拷贝对于引用类型的属性只是引用原对象的内存地址,而深拷贝是所有属性都创建新的对象
冒泡排序两层循环 依次比较相邻的两个元素,如果顺序错误就交换位置
如果有n个元素,需要比较n-1轮,
每一轮需要比较n-i次,i表示第几轮
数组中有length()方法吗,String中有length()方法吗
数组中没有length()方法,有length()属性,String中有length()方法
int和Integer有什么区别
Integer 是int的包装类, int 是基本数据类型
Integer变量必须实例化后,才能使用,int不需要
Integer实际对象的引用,指向此new的Integer对象,int是直接存储数据值的
Integer默认值是null,int的默认值是0
数组和ArrayList的区别
数组是类型安全的,ArrayList是类型非安全的
ArrayList长度可变,数组长度不可变
数组存储的是数据类型,ArrayList存储的是对象,
Arrray和ArrayList的区别
Array可以包含基本数据类型和对象类型,ArrayList只能包含对象类型
Array的大小是固定的,ArrayList的大小是动态的
ArrayList提供了添加,插入或移除, 而Array只能一次获取或设置一个元素值
ArrayList和LinkedList的区别
1. 底层结构 ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于链表的数据结构;
2 .初始长度 Arraylist的初始化默认是10,而LinkedList默认初始化为空
3. 扩容机制 ArrayList实现了List接口,继承了AbstractList抽象类,底层是基于数组实现的并实现的动态扩容 LinkedList是一个继承自AbstractSequentialList的双向链表,因此它也可以被当作堆栈,队列或双端队列进行操作
4. 使用场景 ArrayList使用在查询比较多,但是插入和删除比较少的情况,而LinkedList用在查询比较少而插入删除比较多的情况
抽象类 : 可以定义构造器,可以有抽象方法和具体方法,可以定义成员变量,可以包含静态方法,一个类只能继承一个抽象类,抽象类是不能实例化的类
接口 :不能定义构造器,方法全部是抽象方法,不能有静态方法,接口中定义的成员变量实际上都是常量,一个类可以实现多个接口
相同 :都不能实例化,可以将抽象类和接口类型作为引用类型
阐述静态变量和实例变量的区别
存储区域不同: 静态变量存储在静态存储区,普通变量存储在堆中
静态变量与类有关,实例变量与实例有关
内存分配方式不同
生命周期不同
Error和Exception有什么区别
首先他们都是继承了Throwable类,在Java中只有Throwable类型的实例才可以被抛出或捕获,
EXception: 是程序正常运行中可以预见的意外情况,可以被捕获并处理,
Exception又分为可检查异常(checked)和不检查异常(unchecked)
Error: 表示系统错误,不能通过程序来进行错误处理,
表示恢复不是不可能但很困难的情况下的一种严重问题
try{}里面有个return语句,那么紧跟在这个后面try后的finally{}里面的代码会不会被执行
会执行,在return中间执行,
如果finally中也有return,则会直接返回并终止程序,函数栈中的retrun不会被完成
如果finally中没有return,则在执行完finally中的代码之后,会将函数栈中的try中retrun内容返回并终止程序;
常用的集合
List,Set,Map是否都继承Collection接口
List:有序可重复,
Set:无序,唯一
Map,基于hash表,key不可以重复,
Thread类的sleep()方法和对象的wait()方法都可以让线程暂停,它们有什么区别
相同点:一旦执行,都可以让当前线程进入阻塞状态,
不同点: 两个方法所属类型不同,sleep()属于Thread
wait()属于Objiect,
调用的要求不同: sleep可以在任何需求的场景下调用,wait()必须在同步代码块或者同步方法中调用.
sleep必须捕获异常,而wait,不需要捕获异常
关于是否释放同步锁对象,如果两个方法都使用在同步代码块(同步方法)中,sleep()不会释放同步锁对,而wait()会释放同步锁对象.
==和equals的区别是什么?
区别就是==是运算符,equals是方法
如果比较的是基本数据类型的对象,==比较的是数值是否相等,如果是引用数据类型,比较的是对象地址值是否相等
equals()用来比较方法两个对象的内容相等,且不能比较基本数据类型
什么是反射机制?
在运行状态中对于任意一个类(class)文件都能够知道这个类的属性和方法,
动态获取类中的信息就是java反射机制
反射应用场景
1.用JDBC连接数据库时使用Class,forName通过反射加载数据库的驱动程序
2.Spring框架的IOC创建对象以及AOP功能
3.动态配置实例的属性
什么是代理模式.为啥要用代理模式(也叫做AOP代理)
代理模式是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象,
好处:可以在目标对象实现的基础上增强额外的功能操作,即扩展目标对象的功能
静态代理: 在使用时需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或继承父类
动态代理 : 代理对象,不需要实现接口,代理对象的生成是利用jdk的api,在内存中构建代理对象,动态代理也叫jdk代理接口代理
cglib代理: 也叫做子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展
java*动态代理是利用反射机制生成一个实现代理接口的匿名类
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来
如果目标对象实现了接口,默认情况下会采用JDK的动态代理也 可以强制使用CGLIB实现AOP
如果目标对象没有实现了接口,必须采用CGLIB库
short s1=1,s1=s1+1 有错吗,short s1=1,s1+=1有错吗
对于short s1=1,s1=s1+1由于1是int类型,因此s1+1运算结果是int类型需要强制转换类型才能赋予给short类型,
而short s1=1,s1+=1可以正常编译,因此s1+=1相当于s1=(short)(s1+1)其中隐含了强制类型转换
Spring和SpringBoot有什么不同
Spring: 是一个开源框架,可以接管web层,业务层,dao层,持久层的组件
SpringBoot : 是一个微服务框架,延续了spring框架的核心思想Ioc和Aop,简化了应用的开发和部署,创建了独立的spring应用,尽可能自动配置spring应用,没有代码生成和xml配置要求
+-
-
常见Exception
-
如 RuntimeException
-
IOException (Io异常)
-
SQlException (SQL异常)
-
RuntimeException下的常见异常
NullPointerException ( 空指针异常 )
ClassCastException -强制类型转换异常
IIegalArgumentException传递非法参数异常
ArithmeticException-算术运算异常
IndexOutofBoundsException- 下标异常
Dubbo是一个分布式,高性能透明化的RPC服务框架,提供服务自动注册,自动发现等高效服务治理方案, 可以和Spring框架无缝集成, 但是Dubbo使用的是缺省协议,不适合传送大数据量的服务,比如传文件,传视频等
透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需要简单配置,没有任何api侵入
服务自动注册与发现,不再需要写死服务提供方地址,注册中心
基于接口查询服务提供者的ip地址,并且能够平滑添加或删除服务提供者
软负载均衡及容错机制,可在内网替代f5等硬件负载均衡器,降低成本,减少单点
设置超时时间有两种,
1.在服务提供端设置超时时间: 在Dubbo的用户文档中推荐如果能在服务端多配置就尽量多配置,因为服务提供者比消费者更清楚自己提供的服务特性
2. 服务消费者端,设置超时时间, 如果在消费者端设置了超时时间,以消费者端为主,即优先级更高,因为服务调用方设置超时时间控制性更灵活,如果消费方超时,服务端线程不会停止,会产生警告
Remoting: 网络通信框架, 提供了对多种NIo框架抽象封装,包括同步转异步和请求-响应模式的信息交换方式
Cluster: 服务框架, 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持,
Registry: 服务注册,基于注册中心目录服务,使消费方能动态的查找服务提供者,使地址透明,使提供方可以平滑添加或减少机器
-
Dubbo有哪些注册中心
-
Multicatst注册中心: 不需要任何节点只需要广播地址,就能进行服务注册和发现,基于网络中组播传输实现
-
zookeeper注册中心 :基于分布式协调系统Zookeeper实现采用watch机制实现数据
-
redis注册中心 :基于redis实现,采用key/map存储
-
simple注册中心
-
默认采用Zookeeper注册中心
Zookeeper的数据模型
是一个树形结构,类似于前端开发中tree.js组件
每个节点称之为znode,它可以有子节点也可以有数据
每个节点分为临时节点和永久节点,
每个zk节点都有各自的版本号,可以通过命令行来显示节点信息
每个节点数据发生变化,那么节点的版本号会累加(乐观锁)
每个节点存储的数据不宜过大,几k即可
Zookeeper应用场景
1.数据发布和订阅, 主要应用于配置中心,将相关数据发布到配置中心然后由应用服务去订阅
2.负载均衡 基于域名服务,进行相应的负载,从而达到请求负载到各个应用中
3.命名服务 主要应用于Rpc服务,比如Dubbo等框架
4.分布式锁: 分布式锁是控制分布式系统同步访问共享资源的一种方式,
5.分布式协调/通知
注册中心为啥用nacos,不用Eureka
1.nacos在自动或手动下线服务,使用消息机制通知客户端,服务实例的修改很快响应 Eureka只能通过定时任务剔除无效的服务
2.nacos可以根据namespace命名空间,Datald,Group分组,来区分不同环境 不同配置
MongoDb是什么
是属于文档类型的非关系型数据库包括开源.高性能高可用.可扩展的数据逻辑层次关系
数据类型
null, false和true 数值 数组
日期 正则表达式 嵌套文档 对象
业务场景
网站数据: 非常适合实时的插入.更新与查询
物流场景 使用Mongo存储订单信息订单在运送过程中不断跟新
游戏场景 使用Mongo存储游戏用户信息,用户的转变积分等
也可用于JSON数据的存储: 非常时候文档化格式的存储和查询
NGinx是什么 是一个高性能的HTTP和泛型代理服务器,及电子邮件代理服务器 同时也是一个非常高效的反向代理 负载均衡
作用:
反向代理: 将多态服务器代理成一台服务器
负载均衡: 将多个请求均匀的分配到多台服务器上,减轻服务器的压力,提高服务的吞吐量
动静分离: nginx可以用作静态文件的缓存服务器,提高访问速度
String: 最基本的数据类型,二进制安全的字符串,最大512M
list: 按照添加顺序保存顺序的字符串列表
set: 无序的字符集合,不存在重复的元素
hash: key_value对的一种集合,和java中hashmap类似
zset : 已排序的字符串集合
缓存,热点数据,分布式,session,分布式锁,
计数器,文章的阅读量,微博点赞数等,允许一定的延迟,先写Redis再定时同步到数据库
Redis提供了两种持久化的方式,分别是RDB,和AOF
RDB: 简而言之,就是在不同的时间点,将redsi缓存的数据生成快照存储到磁盘等介质上
AOF: 则是换个角度来实现持久化,就是将redis执行过的所有写指令记录下来, 在下次redsi重新启动时只要把这些写指令从前到后在重复执行一遍,就可以实现数据恢复了
RDB和AOF也可以同时使用,在这种情况下,如果redsi重启的话则会优先采用AOf方式来进行数据恢复,这是应为AOF方式的数据恢复完整度更高
redis 过期键的删除策略(默认采用定期删除+惰性删除策略)
1.定时任务删除, 在设置健的过期时间的同时创建一个定时器, 让定时器在健的过期时间来临时立即执行对健的删除操作
2.惰性删除,放任健过期不管,但是每次从健空间中获取健时,都检查取得的健是否过期,如果过期就删除该健,如果没有就返回该健
3.定期删除,每隔一段时间就对数据库进行一次检查,删除里面的过期健,
定期删除+惰性删除是如何工作的呢
1.定期删除,redis默认每个100ms检查是否有过期的key,有过期的key则删除, 需要说明的是 redis不是每个100ms将所有的key检查异常,而是随机抽取进行检查,因此如果只采用定期删除策略,会导致很多key的时间没有删除
2.于是,惰性删除派上用场了,也就是说在你获取某个key的时候,redsi会检查一次,这个key如果设置了过期时间那么是否过期,如果过期就删除
为什么不采用定时删除策略呢
定时删除用一个定时器来负责监视key,过期则自动删除,虽然内存及时释放,但是十分消耗cpu资源,在大并发请求下,cpu要将时间应用在处理请求,而不是删除key
采用定期删除+惰性删除就没有其他问题吗?
不是的,如果定期删除没删除key,然后你也没及时请求key, 也就是说惰性删除也没生效,这样redis的内存会越来越高,那么就应该采用内存淘汰机制在redis.conf中有一行配置
缓存和数据库双写不一致问题 :请求先是从redis中查询,如果redis中不存在数据就不会走数据库,如果不能保证缓存跟数据库的一致性就会导致请求获取的数据不是最新数据
缓存雪崩问题 即缓存同一时间大面积的失效,这个时候又来一大波请求,结果请求都怼到数据库上,从而导致数据库连接异常
缓存穿透问题: 缓存穿透,即黑客故意去查询缓存中不存在的数据,导致所有的请求怼到数据库上,从而数据库连接异常
缓存高并发问题: 指同时有多个子系统,set同一个key值
缓存和数据库双写不一致: 1. 编写删除缓存的接口,在更新数据库的同时调用删除缓存的接口删除缓存中的数据
2.消息队列:Amq,消息通知
缓存雪崩: 给缓存的失效时间加一个随机值,避免集体失效,
使用互斥锁,或者搭建redsi集群
缓存穿透: 利用互斥锁,缓存失效的时候先去获得锁,得到锁了再去请求数据库,没得到锁,则休眠一段时间重试 或者采用异步更新策略,无论key是否取到值,都直接返回
缓存高并发 : 准备一个分布式锁,让请求都去抢锁,抢到锁就去做set操作
持久化机制
内存淘汰机制与缓存过期处理 清除最少用的旧内存,然后保存新的内存
主从机制 也叫读写分离模式,
哨兵机制 master挂了,继续保证可用性,实现继续试写
redids集群三种方式
1.主从模式 读写分离,负载均衡,故障恢复,数据冗余(实现数据热备份,) 高可用基石(基于主从复制,构建哨兵模式与集群,实现redis的高可用方案)
2.哨兵模式 监控master存活检测,自动故障转移,
3.Cluster模式 主要是集群通信,数据迁移,定时任务等等,
redis内存溢出怎么处理
1.加内存,
2.搭建集群
3.给key设置超时时间,利用内存淘汰机制
1.应用解耦, 生产者只与mq有关系,生产者和消费者之间耦合性是最低的
2.异步提速, 生产者只需要发送消息给mq,不需要等待消费者业务来 完成
3.肖峰填谷 , 峰值降低,低估能让服务继续消费消息
1.单发送单接受: 使用场景,简单的发送和接受 , 没有特别的处理
2.单发送多接受: 一个发送端,多个接收端,如分布式的任务发布,为了保证消息发送的可靠性,不丢失消息,使消息持久化,同时为了防止接收端在处理消息时down掉,只有在消息处理完成后才发烧ack消息
3.发布订阅模式: 发送端发送广播消息,多个接受端接收
4.按topic发送接受 : 发送端不只按固定的routing key发送消息,而按照字符串"匹配"发送,接收端同样如此
生产者提交给消息服务器时,使用确认机制
消息服务器给对应的队列,交换机等都持久化,保证数据的不丢失
消费者采用消息确认机制,保证数据的不丢失
Rabbitmq如何防止消息堆积
1.排查消费者的消费性能瓶颈
2.增加消费的多线程处理
3.部署增加多个消费者 或新增消息队列
1.rabbitmq拆分多个queue,每个queue由一个consumer消费,但是会造成吞吐量降低
2.或者就一个queue但是对应一个consumer,然后这个consumer内部用内存队列做排队,然后分发给底层不同的worker来处理
mq重复消费解决方式
利用数据库的唯一约束,给这个消息做一个唯一主键,避免导致主键冲突,避免数据库出现脏读数据
redis设置全局唯一id 给消息分配一个全局id,并存放在redis里,在消费接口上先在redis中查看是否存在全局id,如果存在,调用消费者并删除全局id,如果不存在,不做后续操作
多版本(乐观锁)机制 给业务数据添加一个版本号,每次更新数据前,比如当前版本和消息中的版本是否一致,如果一致就更新数据并且版本号+1,如果不一致就不更新
kafka消息丢失的场景和解决方案 kafka默认是同步方法
场景 acks=0时,不和kafka集群进行消息接受确认,则当网络异常或者缓存区满了等 消息可能丢失
acks=1时 在同步模式下,只有leader确认接受成功后但挂掉了,副本没有同步,消息可能丢失
解决方案 对于消息丢失,
同步模式下,确认机制设置为-1,即让消息写入leader和Follower之后再确认消息发送成功
异步模式下,为防止缓冲区满,可以在配置文件设置不限制阻塞
对于消息重复 将消息的唯一标识保存到外部介质中,每次消息时判断是否处理过即可
1.拦截器是基于java的反射机制的,而过滤器是基于函数回调的
2.拦截器不依赖与servlet容器,过滤器依赖servlet容器
3.拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用
4.拦截器可以获取IOC容器中的各个bean,而过滤器不行,在拦截器里注入一个service,可以调用业务逻辑
5.拦截器可以访问action上下文,值栈里的对象,而过滤器不能访问
全局注解:
@SpringBootApplication: 标识一个SpringBootg工程,是启动类, 组合注解包括@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan
@Value: 简单属性的依赖注入
@Autowired: 对象属性注入
@ComponentScan: 组件扫描
1.