社招后端21连问(三年工作经验一面)(三)
13. Hashmap 是怎样实现的?为什么要用红黑树,而不用平衡二叉树?为什么在1.8中链表大于8时会转红黑树?HashMap是线性安全的嘛?如何保证安全?
13.1 Hashmap 是怎样实现的?
• JDK1.7 Hashmap的底层数据结构是数组+链表
• JDK1.8 Hashmap的底层数据结构是数组+链表+红黑树
数据元素通过映射关系,即散列函数,映射到桶数组对应索引的位置,插入该位置时,如果发生冲突,从冲突的位置拉一个链表,把冲突元素放到链表。如果链表长度>8且数组大小>=64,链表转为红黑树 如果红黑树节点个数<6 ,转为链表。
13.2 为什么要用红黑树,为什么不用二叉树?为什么不用平衡二叉树?
为什么不用二叉树?
红黑树是一种平衡的二叉树,其插入、删除、查找的最坏时间复杂度都为 O(logn),避免了二叉树最坏情况下的O(n)时间复杂度。
为什么不用平衡二叉树?
平衡二叉树是比红黑树更严格的平衡树,为了保持保持平衡,需要旋转的次数更多,也就是说平衡二叉树保持平衡的效率更低,所以平衡二叉树插入和删除的效率比红黑树要低。
13.3 为什么在1.8中链表大于8时会转红黑树?
红黑树的平均查找长度是log(n),如果长度为8,平均查找长度为log(8)=3,链表的平均查找长度为n/2,当长度为8时,平均查找长度为8/2=4,这才有转换成树的必要;链表长度如果是小于等于6,6/2=3,而log(6)=2.6,虽然速度也很快的,但是转化为树结构和生成树的时间并不会太短。
13.4 HashMap是线性安全的嘛?如何保证安全?
HashMap不是线程安全的,多线程下扩容死循环。可以使用HashTable、Collections.synchronizedMap、以及 ConcurrentHashMap 可以实现线程安全。
• HashTable 是在每个方法加上 synchronized 关键字,粒度比较大;
• Collections.synchronizedMap 是使用 Collections 集合工具的内部类,通过传入 Map 封装出一个 SynchronizedMap 对象,内部定义了一个对象锁,方法内通过对象锁实现;
• ConcurrentHashMap 在jdk1.7中使用分段锁,在jdk1.8中使用CAS+synchronized。
14. select 和 epoll的区别
14.1 IO多路复用之select
应用进程通过调用select函数,可以同时监控多个fd,在select函数监控的fd中,只要有任何一个数据状态准备就绪了,select函数就会返回可读状态,这时应用进程再发起recvfrom请求去读取数据。
非阻塞IO模型(NIO)中,需要N(N>=1)次轮询系统调用,然而借助select的IO多路复用模型,只需要发起一次询问就够了,大大优化了性能。
但是呢,select有几个缺点:
• 监听的IO最大连接数有限,在Linux系统上一般为1024。
• select函数返回后,是通过遍历fdset,找到就绪的描述符fd。(仅知道有I/O事件发生,却不知是哪几个流,所以遍历所有流)
• 因为存在连接数限制,所以后来又提出了poll。与select相比,poll解决了连接数限制问题。但是呢,select和poll一样,还是需要通过遍历文件描述符来获取已经就绪的socket。如果同时连接的大量客户端,在一时刻可能只有极少处于就绪状态,伴随着监视的描述符数量的增长,效率也会线性下降。
14.2 IO多路复用之epoll
为了解决select/poll存在的问题,多路复用模型epoll诞生,它采用事件驱动来实现,流程图如下:epoll先通过epoll_ctl()来注册一个fd(文件描述符),一旦基于某个fd就绪时,内核会采用回调机制,迅速激活这个fd,当进程调用epoll_wait()时便得到通知。这里去掉了遍历文件描述符的坑爹操作,而是采用监听事件回调的机制。这就是epoll的亮点。
一下select、poll、epoll的区别
大家可以看我这篇文章哈:看一遍就理解:IO模型详解
15. http与https的区别,https的原理,如何加密的?
http与https的区别
思路: 这道题实际上考察的知识点是HTTP与HTTPS的区别,这个知识点非常重要,可以从安全性、数据是否加密、默认端口等这几个方面去回答哈。其实,当你理解HTTPS的整个流程,就可以很好回答这个问题啦。
HTTP,即超文本传输协议,是一个基于TCP/IP通信协议来传递明文数据的协议。HTTP会存在这几个问题:
• 请求信息是明文传输,容易被窃听截取。
• 没有验证对方身份,存在被冒充的风险
• 数据的完整性未校验,容易被中间人篡改
为了解决Http存在的问题,Https出现啦。
HTTPS= HTTP+SSL/TLS,可以理解Https是身披SSL(Secure Socket Layer,安全套接层)的HTTP。
HTTP + HTTPS的区别https的原理,如何加密的 • 客户端发起Https请求,连接到服务器的443端口。
• 服务器必须要有一套数字证书(证书内容有公钥、证书颁发机构、失效日期等)。
• 服务器将自己的数字证书发送给客户端(公钥在证书里面,私钥由服务器持有)。
• 客户端收到数字证书之后,会验证证书的合法性。如果证书验证通过,就会生成一个随机的对称密钥,用证书的公钥加密。
• 客户端将公钥加密后的密钥发送到服务器。
• 服务器接收到客户端发来的密文密钥之后,用自己之前保留的私钥对其进行非对称解密,解密之后就得到客户端的密钥,然后用客户端密钥对返回数据进行对称加密,酱紫传输的数据都是密文啦。
• 服务器将加密后的密文返回到客户端。
• 客户端收到后,用自己的密钥对其进行对称解密,得到服务器返回的数据。
16. Raft算法原理
Raft 算法是分布式系统开发首选的共识算法,它通过“一切以领导者为准”的方式,实现一系列值的共识和各节点日志的一致。Raft 算法一共涉及三种角色(Follower、Candidate、Leader)和两个过程(Leader选举和日志复制)。
16.1 Raft 角色
跟随者(Follower):,默默地接收和处理来自Leader的消息,当等待Leader心跳信息超时的时候,就主动站出来,推荐自己当候选人(Candidate)。
候选人(Candidate):向其他节点发送投票请求,通知其他节点来投票,如果赢得了大多数(N/2+1)选票,就晋升领导(Leader)。
领导者(Leader):负责处理客户端请求,进行日志复制等操作,每一轮选举的目标就是选出一个领导者;领导者会不断地发送心跳信息,通知其他节点“我是领导者,我还活着,你们不要发起新的选举,不用找个新领导者来替代我。”
16.2 领导选举过程
1.在初始时,集群中所有的节点都是Follower状态,都被设定一个随机选举超时时间(一般150ms-300ms):
2. 如果Follower在规定的超时时间,都没有收到来自Leader的心跳,它就发起选举:将自己的状态切为 Candidate,增加自己的任期编号,然后向集群中的其它Follower节点发送请求,询问其是否选举自己成为Leader:3.其他节点收到候选人A的请求投票消息后,如果在编号为1的这届任期内还没有进行过投票,那么它将把选票投给节点A,并增加自己的任期编号:
4.当收到来自集群中过半节点的接受投票后,A节点即成为本届任期内 Leader,他将周期性地发送心跳消息,通知其他节点我是Leader,阻止Follower发起新的选举:
16.2 日志复制
当有了leader,系统可以对外工作期啦。客户端的一切请求来发送到leader,leader来调度这些并发请求的顺序,并且保证leader与followers状态的一致性。Leader接收到来自客户端写请求后,处理写请求的过程其实就是一个日志复制的过程。
日志项长什么样呢?如下图:请求完整过程:
1.当系统leader收到一个来自客户端的写请求,就会添加一个log entry(日志项)到本地日志。
2.Leader通过日志复制(AppendEntries)RPC 消息,将日志项并行复制到集群其它Follower节点。
3.如果Leader接收到大多数的“复制成功”响应后,它将日志项应用到自己的状态机,并返回成功给客户端。相反没有收到大多数的“复制成功”响应,那么就返回错误给客户端;
4.当Follower接收到心跳信息,或者新的AppendEntries消息后,如果发现Leader已经提交了某条日志项,而自己还没应用,那么Follower就会将这条日志项应用到本地的状态机中。
Raft算法,Leader是通过强制Follower直接复制自己的日志项,来处理不一致日志,从而最终实现了集群各节点日志的一致。
大家有兴趣可以看这篇文章哈:分布式一致性:Raft算法原理[1]
(https://www.tpvlog.com/article/66)