本文总结了近期面试碰到几乎所有问题
1,分布式的CAP是指什么
- Consistency 一致性:
一致性是数据在各个副本之间是否能够保持一致的特性。比如更新操作成功并返回客户端后,所有节点在同一时间的数据完全一致。
- Avalilability 可用性:
指系统提供的服务必须一致处于可用的状态。好的可用性主要是指系统能够很好的为用户服务,不出现用户操作失败或者访问超时等用户体验不好的情况。
- Partition Toleranc 分区容错性:
分布式系统在遇到任何网络分区的故障时候。仍然需要能够保证对外提供满足一致性和可用性的服务。分区容错性要求能够使应用虽然是一个分布式系统,而看上去却好像是在一个可以运转正常的整体。比如现在的分布式系统中有某一个或者几个机器宕掉了,其他剩下的机器还能够正常运转满足系统需求,对于用户而言并没有什么体验上的影响。
任何分布式系统都不可能同时满足以上三个条件,那么取舍的策略就共有三种:
CA without P:如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但放弃P的同时也就意味着放弃了系统的扩展性,也就是分布式节点受限,没办法部署子节点,这是违背分布式系统设计的初衷的。
CP without A:如果不要求A(可用),相当于每个请求都需要在服务器之间保持强一致,而P(分区)会导致同步时间无限延长(也就是等待数据同步完才能正常访问服务),一旦发生网络故障或者消息丢失等情况,就要牺牲用户的体验,等待所有数据全部一致了之后再让用户访问系统。设计成CP的系统其实不少,最典型的就是分布式数据库,如Redis、HBase等。对于这些分布式数据库来说,数据的一致性是最基本的要求,因为如果连这个标准都达不到,那么直接采用关系型数据库就好,没必要再浪费资源来部署分布式数据库。
AP wihtout C:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。典型的应用就如某米的抢购手机场景,可能前几秒你浏览商品的时候页面提示是有库存的,当你选择完商品准备下单的时候,系统提示你下单失败,商品已售完。这其实就是先在 A(可用性)方面保证系统可以正常的服务,然后在数据的一致性方面做了些牺牲,虽然多少会影响一些用户体验,但也不至于造成用户购物流程的严重阻塞。
2,事务acid的实现原理
事务的 ACID 是通过 InnoDB 日志和锁来保证。事务的隔离性是通过数据库锁的机制实现的,持久性通过 Redo Log(重做日志)来实现,原子性和一致性通过 Undo Log 来实现。
Undo Log 的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为 Undo Log)。然后进行数据的修改。如果出现了错误或者用户执行了 Rollback 语句,系统可以利用 Undo Log 中的备份将数据恢复到事务开始之前的状态。
和 Undo Log 相反,Redo Log 记录的是新数据的备份。在事务提交前,只要将 Redo Log 持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是 Redo Log 已经持久化。系统可以根据 Redo Log 的内容,将所有数据恢复到最新的状态。
3,分布式事务有没有了解
简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。
本质上来说,分布式事务就是为了保证不同数据库的数据一致性。(最终一致性)
4,分布式事务解决方案(现有解决框架,lcn)
两阶段提交协议 2PC(分布式事务协议)
分布式系统的一个难点是如何保证架构下多个节点在进行事务性操作的时候保持一致性。为实现这个目的,二阶段提交算法的成立基于以下假设:
该分布式系统中,存在一个节点作为协调者(Coordinator),其他节点作为参与者(Cohorts)。且节点之间可以进行网络通信。
所有节点都采用预写式日志,且日志被写入后即被保持在可靠的存储设备上,即使节点损坏不会导致日志数据的消失。
所有节点不会永久性损坏,即使损坏后仍然可以恢复。
1. 第一阶段(投票阶段):
协调者节点向所有参与者节点询问是否可以执行提交操作(vote),并开始等待各参与者节点的响应。
参与者节点执行询问发起为止的所有事务操作,并将Undo信息和Redo信息写入日志。(注意:若成功这里其实每个参与者已经执行了事务操作)
各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个”同意”消息;如果参与者节点的事务操作实际执行失败,则它返回一个”中止”消息。
2. 第二阶段(提交执行阶段):
当协调者节点从所有参与者节点获得的相应消息都为”同意”时:
协调者节点向所有参与者节点发出”正式提交(commit)”的请求。
参与者节点正式完成操作,并释放在整个事务期间内占用的资源。
参与者节点向协调者节点发送”完成”消息。
协调者节点受到所有参与者节点反馈的”完成”消息后,完成事务。
如果任一参与者节点在第一阶段返回的响应消息为”中止”,或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:
协调者节点向所有参与者节点发出”回滚操作(rollback)”的请求。
参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。
参与者节点向协调者节点发送”回滚完成”消息。
协调者节点受到所有参与者节点反馈的”回滚完成”消息后,取消事务。
不管最后结果如何,第二阶段都会结束当前事务。
二阶段提交看起来确实能够提供原子性的操作,但是不幸的事,二阶段提交还是有几个缺点的:
执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。
参与者发生故障。协调者需要给每个参与者额外指定超时机制,超时后整个事务失败。(没有多少容错机制)
协调者发生故障。参与者会一直阻塞下去。需要额外的备机进行容错。(这个可以依赖后面要讲的Paxos协议实现HA)
二阶段无法解决的问题:协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。
5,可重入锁(ReentrantLock):
ReentrantLock中支持两种获取锁的方式,一种是公平模型,一种是非公平模型
公平锁模型:
初始化时, state=0,表示没有线程获取到锁,然后A线程请求并获取到了锁,state+1,这时候state被改为1,A线程继续执行其他任务;这时B线程来请求锁,因为A线程此时获取了锁并且正在执行,生成节点进行排队;(初始化的时候,会生成一个空的头节点,然后才是B线程节点,原因未探究)这时候,如果线程A又请求锁,是否需要排队?答案当然是否定的,否则就直接死锁了。当A再次请求锁,会增加state状态值,同理,当A每次释放锁,会让state状态值-1,只有当state状态值变为0时,才是真正的释放了锁,此时等待列队中B线程节点会被唤醒获取锁并从等待队列中删除,假如此时B线程后面还有C线程,它会继续沉睡,直至B线程执行完毕释放锁。
非公平锁模型:
当线程A执行完之后,要唤醒线程B是需要时间的,而且线程B醒来后还要再次竞争锁,所以如果在切换过程当中,来了一个线程C,那么线程C是有可能获取到锁的,如果C获取到了锁,B就只能继续休眠了。
6,mysql优化
避免 SELECT *
避免在 where 子句中使用!=或<>操作符,否则引擎放弃使用索引而进行全表扫描。
like 语句避免前置百分号,前置百分号会导致索引失效
添加适当索引(index) [四种: 普通索引、主键索引、唯一索引unique、全文索引]
分库分表
读写分离:主从复制,主数据库用来写操作,从数据库同时进行复制并提供读数据操作;可以主数据库使用innoDB引擎,从数据库使用myisam(读操作更快)
存储过程
对mysql配置优化 [配置最大并发数my.ini, 调整缓存大小 ]
7,mysql索引的优点和缺点
优点:
1、所有的 MySql 列类型(字段类型)都可以被索引,也就是可以给任意字段设置索引
2、大大加快数据的查询速度
缺点:
1、创建索引和维护索引要耗费时间,并且随着数据量的增加所耗费的时间也会增加
2、索引也需要占空间,我们知道数据表中的数据也会有最大上线设置的,如果我们有大量的索引,索引文件可能会比数据文件更快达到上线值
3、当对表中的数据进行增加、删除、修改时,索引也需要动态的维护,降低了数据的维护速度。
8,mysql索引分类
普通索引
MySQL 中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一点。Index(xx) 或者 key(xx)
唯一索引
索引列中的值必须是唯一的,但是允许为空值,UNIQUE INDEX UniqIdx(xx)
主键索引
是一种特殊的唯一索引,不允许有空值。PRIMARY KEY(id)
组合索引
在表中的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,使用组合索引时遵循最左前缀集合。
全文索引
全文索引,只有在 MyISAM 引擎上才能使用,只能在 CHAR,VARCHAR,TEXT 类型字段上使用全文索引。全文索引就是在一堆文字中,通过其中的某个关键字等,就能找到该字段所属的记录行,比如有"你是个大牛,神人 ..." 通过大牛,可能就可以找到该条记录。这里说的是可能,因为全文索引的使用涉及了很多细节,我们只需要知道这个大概意思。FULLTEXT INDEX FullTxtIdx(info) SELECT * FROM t4 WHERE MATCH(info) AGAINST('gorlr');
空间索引
只有在 MyISAM 引擎上才能使用,空间索引是对空间数据类型的字段建立的索引,MySQL 中的空间数据类型有四种,GEOMETRY、POINT、LINESTRING、POLYGON。在创建空间索引时,使用 SPATIAL 关键字。创建空间索引的列,必须将其声明为 NOT NULL。。SPATIAL INDEX spatIdx(g)
9,使用联合索引需要注意什么
最左前缀原则,比如建立索引,由 id、name 和 age3 个字段构成的索引,索引行中就按 id/name/age 的顺序存放,索引可以索引下面字段组合(id,name,age)、(id,name)或者(id)。如果要查询的字段不构成索引最左面的前缀,那么就不会是用索引,比如,age 或者(name,age)组合就不会使用索引查询
10,SpringMVC执行流程(工作原理)
1、用户发送请求至前端控制器DispatcherServlet。
2、DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、DispatcherServlet调用HandlerAdapter处理器适配器。
5、HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6、Controller执行完成返回ModelAndView。
7、HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8、DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9、ViewReslover解析后返回具体View。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet响应用户。
11,集合框架脑图
单列(collection)、双列(map)
单列:List、Set、Queue
List:ArrayList、Vector、LinkedList
Set:HashSet、TreeSet、LinkedHashSet
Queue:在两端出入的List,也可以用数组或者链表实现
双列:HashMap、HashTable、TreeMap
12,List和Set区别
List,Set 都是继承自 Collection 接口。
List 特点:元素有放入顺序,元素可重复。
Set 特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉。
注意:元素虽然无放入顺序,但是元素在 Set 中的位置是有该元素的 hashcode 决定的,其位置其实是固定的。
另外 List 支持 for 循环,也就是通过下标来遍历,也可以用迭代器,但是 Set 只能用迭代,因为他无序,无法用下标来取得想要的值。
Set 和 List 对比:
Set:检索元素效率高,删除和插入效率低,插入和删除不会引起元素位置改变。
List:和数组类似,List 可以动态增长,查找元素效率低,插入删除元素效率,因为可能会引起其他元素位置改变。
13,List和Map的区别
List 是对象集合,允许对象重复。
Map 是键值对的集合,不允许 key 重复。
14,Array 和 ArrayList 有何区别?什么时候更适合用 Array?
Array 可以容纳基本类型和对象,而 ArrayList 只能容纳对象。
Array 是指定大小的,而 ArrayList 大小是固定的,可自动扩容。
Array 没有提供 ArrayList 那么多功能,比如 addAll、removeAll 和 iterator 等。
尽管 ArrayList 明显是更好的选择,但也有些时候 Array 比较好用,比如下面的三种情况。
1、如果列表的大小已经指定,大部分情况下是存储和遍历它们
2、对于遍历基本数据类型,尽管 Collections 使用自动装箱来减轻编码任务,在指定大小的基本类型的列表上工作也会变得很慢。
3、如果你要使用多维数组,使用 [][] 比 List 会方便。
15,ArrayList 与 LinkedList 区别?
ArrayList
优点:ArrayList 是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。
缺点:因为地址连续,ArrayList 要移动数据,所以插入和删除操作效率比较低。
LinkedList
优点:LinkedList 基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址。对于新增和删除操作 add 和 remove ,LinedList 比较占优势。LinkedList 适用于要头尾操作或插入指定位置的场景。
缺点:因为 LinkedList 要移动指针,所以查询操作性能比较低。
适用场景分析:
当需要对数据进行对随机访问的情况下,选用 ArrayList 。
当需要对数据进行多次增加删除修改时,采用 LinkedList 。
如果容量固定,并且只会添加到尾部,不会引起扩容,优先采用 ArrayList 。
当然,绝大数业务的场景下,使用 ArrayList 就够了。主要是,注意好避免 ArrayList 的扩容,以及非顺序的插入。
16,ArrayList实现原理、初始容量以及它的扩容机制
实现原理:ArrayList底层是通过动态数组实现的
初始容量及扩容机制:如果通过无参构造的话,初始数组容量为 0 ,当真正对数组进行添加时,才真正分配容量(10)。每次按照 1.5 倍(位运算)的比率通过 copyOf 的方式扩容。在 JKD6 及之前,如果通过无参构造的话,初始数组容量为10,每次通过 copyOf 的方式扩容后容量为原来的 1.5 倍。
17, ArrayList 集合加入 10 万条数据,应该怎么提高效率?
ArrayList 的默认初始容量为 10 ,要插入大量数据的时候需要不断扩容,而扩容是非常影响性能的。因此,现在明确了 10 万条数据了,我们可以直接在初始化的时候就设置 ArrayList 的容量(经过测试,十万条数据插入指定初始容量为100000大约比无参构造情况下快2~3毫秒,100万条数据快大约7~9毫秒【此处插入数据均为int整数】)
18,HashMap 和 Hashtable 的区别?
HashMap是线程不安全的,HashTable是线程安全的,hashmap允许键值为空
19,HashMap 和 ConcurrentHashMap 的区别
ConcurrentHashMap 是线程安全的 HashMap 的实现。主要区别如下:
1、ConcurrentHashMap 对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用 lock 锁进行保护,相对 于Hashtable 的 syn 关键字锁的粒度更精细了一些,并发性能更好。而 HashMap 没有锁机制,不是线程安全的。
JDK8 之后,ConcurrentHashMap 启用了一种全新的方式实现,利用 CAS 算法。
2、HashMap 的键值对允许有 null ,但是 ConCurrentHashMap 都不允许。
20,TCP协议三次握手
TCP协议在发包的时候,会进行分包,seq是包 的序号,ack是下一个包的序号,SYN=1表示请求,ACK=1表示确定。
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
21,多线程有什么优缺点?
1)好处
使用多线程可以把程序中占据时间长的任务放到后台去处理,如图片、视屏的下载。
发挥多核处理器的优势,并发执行让系统运行的更快、更流畅,用户体验更好。
2)坏处
大量的线程降低代码的可读性。
更多的线程需要更多的内存空间。
当多个线程对同一个资源出现争夺时候要注意线程安全的问题。
22,线程状态
线程的生命周期?
线程一共有五个状态,分别如下:
新建(new):当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。例如:Thread t1 = new Thread() 。
可运行(runnable):线程对象创建后,其他线程(比如 main 线程)调用了该对象的 start 方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取 cpu 的使用权。例如:t1.start() 。有些文章,会称可运行(runnable)为就绪,意思是一样的。
运行(running):线程获得 CPU 资源正在执行任务(#run() 方法),此时除非此线程自动放弃 CPU 资源或者有优先级更高的线程进入,线程将一直运行到结束。
死亡(dead):当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
自然终止:正常运行完 #run()方法,终止。
异常终止:调用 #stop() 方法,让一个线程终止运行。
堵塞(blocked):由于某种原因导致正在运行的线程让出 CPU 并暂停自己的执行,即进入堵塞状态。直到线程进入可运行(runnable)状态,才有机会再次获得 CPU 资源,转到运行(running)状态。阻塞的情况有三种:
正在睡眠:调用 #sleep(long t) 方法,可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入可运行(runnable)状态。
正在等待:调用 #wait() 方法。调用 notify() 方法,回到就绪状态。被另一个线程所阻塞:调用 #suspend() 方法。调用 #resume() 方法,就可以恢复。
23,创建线程的方式
方式一,继承 Thread 类创建线程类。
方式二,通过 Runnable 接口创建线程类。
方式三,通过 Callable 和 Future 创建线程。
24,start 和 run 方法有什么区别?
当你调用 start 方法时,你将创建新的线程,并且执行在 run 方法里的代码。
但是如果你直接调用 run 方法,它不会创建新的线程也不会执行调用线程的代码,只会把 run 方法当作普通方法去执行。
25,Thread类的 sleep 方法和对象的 wait 方法都可以让线程暂停执行,它们有什么区别?
sleep 方法,是线程类 Thread 的静态方法。调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态)
wait 方法,是 Object 类的方法。调用对象的 #wait() 方法,会导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的 #notify() 方法(或#notifyAll()方法)时,才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
26, notify 和 notifyAll 有什么区别?
当一个线程进入 wait 之后,就必须等其他线程 notify/notifyAll 。
使用 notifyAll,可以唤醒所有处于 wait 状态的线程,使其重新进入锁的争夺队列中,而 notify 只能唤醒一个。
如果没把握,建议 notifyAll ,防止 notify 因为信号丢失而造成程序错误。
27,线程池满了怎么办
如果你使用的 LinkedBlockingQueue,也就是无界队列的话,没关系,继续添加任务到阻塞队列中等待执行,因为 LinkedBlockingQueue 可以近乎认为是一个无穷大的队列,可以无限存放任务。
如果你使用的是有界队列比方说 ArrayBlockingQueue 的话,任务首先会被添加到 ArrayBlockingQueue 中,ArrayBlockingQueue满了,则会使用拒绝策略 RejectedExecutionHandler 处理满了的任务,默认是 AbortPolicy 。
关于corePoolSize、maxPoolSize、queueCapacity之间的关系:
corePoolSize为初始线程个数,当corePoolSize的线程都在执行中时,
则将Runnable临时放入queueCapacity的缓冲队列中等待,当queueCapacity满了时,
才会将线程个数从corePoolSize扩展至maxPoolSize,
如果此时queueCapacity缓存队列仍然是满的,
则后续Runnable对象加入其中时就会被abort抛弃。
28,ThreadPoolExecutor拒绝策略
ThreadPoolExecutor 默认有四个拒绝策略:
ThreadPoolExecutor.AbortPolicy() ,直接抛出异常 RejectedExecutionException 。
ThreadPoolExecutor.CallerRunsPolicy() ,直接调用 run 方法并且阻塞执行。
ThreadPoolExecutor.DiscardPolicy() ,直接丢弃后来的任务。
ThreadPoolExecutor.DiscardOldestPolicy() ,丢弃在队列中队首的任务。
29,线程池原理
1,判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。
2、线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
3、判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。
30,什么是 Executor 框架?
Executor 框架,是一个根据一组执行策略调用,调度,执行和控制的异步任务的框架。
无限制的创建线程,会引起应用程序内存溢出。所以创建一个线程池是个更好的的解决方案,因为可以限制线程的数量并且可以回收再利用这些线程。利用 Executor 框架,可以非常方便的创建一个线程池。
31,为什么使用 Executor 框架?
每次执行任务创建线程 new Thread() 比较消耗性能,创建一个线程是比较耗时、耗资源的。
调用 new Thread() 创建的线程缺乏管理,被称为野线程,而且可以无限制的创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪,还有线程之间的频繁交替也会消耗很多系统资源。
接使用 new Thread() 启动的线程不利于扩展,比如定时执行、定期执行、定时定期执行、线程中断等都不便实现。
32,创建线程池的几种方式?
普通任务线程池
1、newFixedThreadPool(int nThreads) 方法,创建一个固定长度的线程池。每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化。当线程发生未预期的错误而结束时,线程池会补充一个新的线程。
2、newCachedThreadPool() 方法,创建一个可缓存的线程池。如果线程池的规模超过了处理需求,将自动回收空闲线程。当需求增加时,则可以自动添加新线程。线程池的规模不存在任何限制。
3、newSingleThreadExecutor() 方法,创建一个单线程的线程池。它创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代它。它的特点是,能确保依照任务在队列中的顺序来串行执行。
定时任务线程池
4、newScheduledThreadPool(int corePoolSize) 方法,创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似 Timer 。
5、newSingleThreadExecutor() 方法,创建了一个固定长度为 1 的线程池,而且以延迟或定时的方式来执行任务,类似 Timer
33,Zookeeper 的选举过程?
当 Leader 崩溃,或者 Leader 失去大多数的 Follower,这时 Zookeeper 进入恢复模式,恢复模式需要重新选举出一个新的 Leader,让所有的 Server 都恢复到一个正确的状态。
Zookeeper 的选举算法有两种:一种是基于 basic paxos 实现的,另外一种是基于 fast paxos 算法实现的。系统默认的选举算法为 fast paxos 。
1、选举线程由当前 Server 发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的 Server 。
2、选举线程首先向所有 Server 发起一次询问(包括自己)。
3、选举线程收到回复后,验证是否是自己发起的询问(验证 zxid 是否一致),然后获取对方的 id(myid),并存储到当前询问对象列表中,最后获取对方提议的 Leader相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中。
4、收到所有 Server 回复以后,就计算出 zxid 最大的那个 Server ,并将这个 Server 相关信息设置成下一次要投票的 Server 。
5、线程将当前 zxid 最大的 Server 设置为当前 Server 要推荐的 Leader ,如果此时获胜的 Server 获得 n/2+1 的 Server 票数,设置当前推荐的 Leader 为获胜的 Server ,将根据获胜的 Server 相关信息设置自己的状态,否则,继续这个过程,直到 Leader 被选举出来。
通过流程分析我们可以得出:要使 Leader 获得多数 Server 的支持,则 Server 总数必须是奇数 2n+1 ,且存活的 Server 的数目不得少于n+1 。每个 Server 启动后都会重复以上流程。
34,高并发处理
数据库高并发解决方案:数据库优化、读写分离、分库分表、redis缓存
应用高并发解决方案:nginx负载均衡
35,SpringBoot初始化流程
调用了SpringApplication的静态方法run。这个run方法会构造一个SpringApplication的实例,然后再调用这里实例的run方法就表示启动SpringBoot。
构造SpringApplication的实例
调用SpringApplication.run()方法
构造SpringApplicationRunListeners 实例
发布ApplicationStartedEvent事件
SpringApplicationRunListeners 实例准备环境信息
创建ApplicationContext对象
ApplicationContext实例准备环境信息
刷新的上下文
36,SpringBoot常用注解
@SpringBootApplication:包含@Configuration、@EnableAutoConfiguration、@ComponentScan通常用在主类上。
@Repository:用于标注数据访问组件,即DAO组件。
@Service:用于标注业务层组件。
@RestController:用于标注控制层组件(如struts中的action),包含@Controller和@ResponseBody。
@ResponseBody:表示该方法的返回结果直接写入HTTP response body中,一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上@responsebody后,会直接返回json数据。
@Component:泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
@ComponentScan:组件扫描。个人理解相当于
@Configuration:指出该类是 Bean 配置的信息源,相当于XML中的
@Bean:相当于XML中的
@EnableAutoConfiguration:让 Spring Boot 根据应用所声明的依赖来对 Spring 框架进行自动配置,一般加在主类上。
@AutoWired:byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。当加上(required=false)时,就算找不到bean也不报错。
@Qualifier:当有多个同一类型的Bean时,可以用@Qualifier("name")来指定。与@Autowired配合使用
@Resource(name="name",type="type"):没有括号内内容的话,默认byName。与@Autowired干类似的事。
@RequestMapping:RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
该注解有六个属性:params:指定request中必须包含某些参数值是,才让该方法处理。headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。value:指定请求的实际地址,指定的地址可以是URI Template 模式method:指定请求的method类型, GET、POST、PUT、DELETE等 ;consumes:指定处理请求的提交内容类型(Content-Type),如application/json,text/html;produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
@RequestParam:用在方法的参数前面。@RequestParam String a =request.getParameter("a")。
@PathVariable:路径变量。参数与大括号里的名字一样要相同。
@Profiles Spring Profiles提供了一种隔离应用程序配置的方式,并让这些配置只能在特定的环境下生效。任何@Component或@Configuration都能被@Profile标记,从而限制加载它的时机。
37,SpringBoot中如何定义一个filter
可以使用@webfilter注解后面括号内标注filter名称及拦截路径并且实现Filter类重写init,dofilter,destory方法,@ServletComponentScan 所扫描的包路径必须包含该过滤器
38,AOP使用场景
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging 调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence 持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务
39,如何查看一条sql是否使用索引
Explain select。。。
40,怎么查看sql有多少个事务
查询事务
SELECT * FROM information_schema.INNODB_TRX;
查询正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
查询等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
查询进程
show PROCESSLIST;
查询是否锁表
show OPEN TABLES where In_use > 0;
41,mysql如何设置它的隔离级别
1.查看
SELECT @@tx_isolation
2.设置
2.1所有级别
1)read uncommitted : 读取尚未提交的数据 :哪个问题都不能解决
2)read committed:读取已经提交的数据 :可以解决脏读 ---- oracle默认的
3)repeatable read:重读读取:可以解决脏读 和 不可重复读 ---mysql默认的
4)serializable:串行化:可以解决 脏读 不可重复读 和 虚读---相当于锁表
2.2 设置
设置mysql的隔离级别:setsession transaction isolation level 设置事务隔离级别
42,RabbitMQ的exchange有哪几种类型(topic,direct。。。)
topic:符合条件的queue才能收到消息
direct:指定的queue才能收到消息
fanout:所有绑定到exchange的queue都可以接受消息
routingKey+queue分发:工作队列,多个worker处理不同的任务
queue:生产消费者模式,一发一收
43,RabbitMQ怎么设置TTL
RabbitMQ可以对消息和队列设置TTL. 目前有两种方法可以设置。第一种方法是通过队列属性设置,队列中所有消息都有相同的过期时间。第二种方法是对消息进行单独设置,每条消息TTL可以不同。如果上述两种方法同时使用,则消息的过期时间以两者之间TTL较小的那个数值为准。消息在队列的生存时间一旦超过设置的TTL值,就称为dead message, 消费者将无法再收到该消息。
44,JDK1.8新特性
Lambda表达式 - Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中。
方法引用 - 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使用语言的构造更紧凑简洁,减少冗余代码。
默认方法 - 默认方法就是一个在接口里面有了一个实现的方法。
新工具 - 新的编译工具,如:Nashorn引擎jjs,类依赖分析器jdeps。
Stream API - 新添加的Stream API(java.util.stream)把真正的函数式编程风格引入到Java中。
Date Time API - 加强对日期与时间的处理。
可选类 - 可选类已经成为Java 8类库的一部分,用来解决空指针异常。
Nashorn,JavaScript引擎 - Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。
45,RabbitMQ如何保证消息安全
RabbitMQ提供了持久化的机制,将内存中的消息持久化到硬盘上,即使重启RabbitMQ,消息也不会丢失。但是,仍然有一个非常短暂的时间窗口(RabbitMQ收到消息还没来得及存到硬盘上)会导致消息丢失 要使用RabbitMQ的消息持久化,在声明队列时设置一个参数即可 durable=true
46,分库分表
1.为什么要分表:当一张表的数据达到几千万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了。分表的目的就在于此,减小数据库的负担,缩短查询时间。mysql中有一种机制是表锁定和行锁定,是为了保证数据的完整性。表锁定表示你们都不能对这张表进行操作,必须等我对表操作完才行。行锁定也一样,别的sql必须等我对这条数据操作完了,才能对这条数据进行操作。
2. mysql proxy:做mysql集群,利用amoeba。从上层的java程序来讲,不需要知道主服务器和从服务器的来源,即主从数据库服务器对于上层来讲是透明的。可以通过amoeba来配置。
3.大数据量并且访问频繁的表,将其分为若干个表,比如对于某网站平台的数据库表-公司表,数据量很大,这种能预估出来的大数据量表,我们就事先分出个N个表,这个N是多少,根据实际情况而定。 某网站现在的数据量至多是5000万条,可以设计每张表容纳的数据量是500万条,也就是拆分成10张表,那么如何判断某张表的数据是否容量已满呢?可以在程序段对于要新增数据的表,在插入前先做统计表记录数量的操作,当<500万条数据,就直接插入,当已经到达阀值,可以在程序段新创建数据库表(或者已经事先创建好),再执行插入操作。
4. 利用merge存储引擎来实现分表 如果要把已有的大数据量表分开比较痛苦,最痛苦的事就是改代码,因为程序里面的sql语句已经写好了。用merge存储引擎来实现分表, 这种方法比较适合.
47,linux命令
ps命令——查看静态的进程统计信息(一般结合选项使用 ps aux 或 ps -elf 命令)
建议使用 ps -elf 查询,输出的信息更详细些,包括 PPID (对应的父进程 的PID 号)
top 命令——查看进程动态信息(以全屏交互式的界面显示进程排名,及时跟踪系统资源占用情况)
lsof -i:端口号 用于查看某一端口的占用情况,比如查看8000端口使用情况,lsof -i:8000
48,JDK1.8 JVM运行时数据区域概览
1.8同1.7比,最大的差别就是:元数据区取代了永久代。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元数据空间并不在虚拟机中,而是使用本地内存。
JDK8 后用元空间替代了 Perm Space ;字符串常量存放到堆内存中。
MetaSpace 大小默认没有限制,一般根据系统内存的大小。JVM 会动态改变此值。
可以通过 JVM 参数配置
-XX:MetaspaceSize : 分配给类元数据空间(以字节计)的初始大小(Oracle 逻辑存储上的初始高水位,the initial high-water-mark)。此值为估计值,MetaspaceSize 的值设置的过大会延长垃圾回收时间。垃圾回收过后,引起下一次垃圾回收的类元数据空间的大小可能会变大。
-XX:MaxMetaspaceSize :分配给类元数据空间的最大值,超过此值就会触发Full GC 。此值默认没有限制,但应取决于系统内存的大小,JVM 会动态地改变此值。
为什么要废弃永久代?
1)现实使用中易出问题。
由于永久代内存经常不够用或发生内存泄露,爆出异常 java.lang.OutOfMemoryError: PermGen 。
字符串存在永久代中,容易出现性能问题和内存溢出。
类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
2)永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
3)Oracle 可能会将HotSpot 与 JRockit 合二为一。
Java 内存堆和栈区别?
栈内存用来存储基本类型的变量和对象的引用变量;堆内存用来存储Java中的对象,无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中。
栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存;堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。
如果栈内存没有可用的空间存储方法调用和局部变量,JVM 会抛出 java.lang.StackOverFlowError 错误;如果是堆内存没有可用的空间存储生成的对象,JVM 会抛出 java.lang.OutOfMemoryError 错误。
栈的内存要远远小于堆内存,如果你使用递归的话,那么你的栈很快就会充满。-Xss 选项设置栈内存的大小,-Xms 选项可以设置堆的开始时的大小。
总结来说:JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。
49,Spring中拦截器和过滤器有什么区别
拦截器 :是在面向切面编程的就是在你的service或者一个方法,前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。
过滤器:是在javaweb中,你传入的request、response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者 struts的action前统一设置字符集,或者去除掉一些非法字符.。
拦截器和过滤器比较
①拦截器是基于Java的反射机制的,而过滤器是基于函数回调。
②拦截器不依赖与servlet容器,依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。过滤器依赖与servlet容器。
③拦截器只能对action(也就是controller)请求起作用,而过滤器则可以对几乎所有的请求起作用,并且可以对请求的资源进行起作用,但是缺点是一个过滤器实例只能在容器初始化时调用一次。
④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑
拦截器的实现
1.编写拦截器类实现HandlerInterceptor接口
三个必须实现的方法
preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2)
(第一步:在请求被处理之前进行调用 是否需要将当前的请求拦截下来,如果返回false,请求将会终止,返回true,请求将会继续 Object arg2表示拦截的控制器的目标方法实例)
当进入拦截器链中的某个拦截器,并执行preHandle方法后
postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,ModelAndView arg3)
(第二步:在请求被处理之后进行调用ModelAndView arg3是指将被呈现在网页上的对象,可以通过修改这个对象实现不同角色跳向不同的网页或不同的消息提示)
afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,Exception arg3)
(第三步:在请求结束之后调用 一般用于关闭流、资源连接等 比较少用)
50,抽象类和接口有什么区别?
从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。
Java 提供和支持创建抽象类和接口。它们的实现有共同点,不同点在于:接口中所有的方法隐含的都是抽象的,而抽象类则可以同时包含抽象和非抽象的方法。
类可以实现很多个接口,但是只能继承一个抽象类。类可以不实现抽象类和接口声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
抽象类可以在不提供接口方法实现的情况下实现接口。
Java 接口中声明的变量默认都是 final 的。抽象类可以包含非 final 的变量。
Java 接口中的成员函数默认是 public 的。抽象类的成员函数可以是 private,protected 或者是 public 。
接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是,如果它包含 #main(String[] args) 方法的话是可以被调用的。
51,加密算法
一、对称加密算法
在RSA算法出现之前,人们一直用的是对称加密算法,什么是对称加密算法:
加解密双方使用同一套密钥,即甲用密钥加密,乙还得用与甲同样的密钥来减密,这就存在极大的安全隐患。
常用的对称加减密算法有:DES, 3DES,AES,SM1(国密中的对称算法,密钥长度是128位)等。
二、非对称加密算法
针对对称算法的不足,后来有三位大牛想出了一套非对称算法,也就是现在我们常说的RSA算法。这个算法里用到了很多数学知识非对称加密的典型应用是数字签名。
常见的非对称加密算法有:RSA、ECC(移动设备用)、Diffie-Hellman、El Gamal、DSA(数字签名用)
Hash算法(摘要算法)
Hash算法特别的地方在于它是一种单向算法,用户可以通过hash算法对目标信息生成一段特定长度的唯一hash值,却不能通过这个hash值重新获得目标信息。因此Hash算法常用在不可还原的密码存储、信息完整性校验等。常见的Hash算法有MD2、MD4、MD5、HAVAL、SHA
52,java对象创建过程
Java 中对象的创建就是在堆上分配内存空间的过程,此处说的对象创建仅限于 new 关键字创建的普通 Java 对象,不包括数组对象的创建。
1)检测类是否被加载
当虚拟机遇到 new 指令时,首先先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,就执行类加载过程。
2)为对象分配内存
类加载完成以后,虚拟机就开始为对象分配内存,此时所需内存的大小就已经确定了。只需要在堆上分配所需要的内存即可。
具体的分配内存有两种情况:第一种情况是内存空间绝对规整,第二种情况是内存空间是不连续的。
对于内存绝对规整的情况相对简单一些,虚拟机只需要在被占用的内存和可用空间之间移动指针即可,这种方式被称为“指针碰撞”。
对于内存不规整的情况稍微复杂一点,这时候虚拟机需要维护一个列表,来记录哪些内存是可用的。分配内存的时候需要找到一个可用的内存空间,然后在列表上记录下已被分配,这种方式成为“空闲列表”。
多线程并发时会出现正在给对象 A 分配内存,还没来得及修改指针,对象 B 又用这个指针分配内存,这样就出现问题了。解决这种问题有两种方案:
第一种,是采用同步的办法,使用 CAS 来保证操作的原子性。
另一种,是每个线程分配内存都在自己的空间内进行,即是每个线程都在堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB),分配内存的时候再TLAB上分配,互不干扰。可以通过 -XX:+/-UseTLAB 参数决定。
3)为分配的内存空间初始化零值
对象的内存分配完成后,还需要将对象的内存空间都初始化为零值,这样能保证对象即使没有赋初值,也可以直接使用。
4)对对象进行其他设置
分配完内存空间,初始化零值之后,虚拟机还需要对对象进行其他必要的设置,设置的地方都在对象头中,包括这个对象所属的类,类的元数据信息,对象的 hashcode ,GC 分代年龄等信息。
5)执行 init 方法
执行完上面的步骤之后,在虚拟机里这个对象就算创建成功了,但是对于 Java 程序来说还需要执行 init 方法才算真正的创建完成,因为这个时候对象只是被初始化零值了,还没有真正的去根据程序中的代码分配初始值,调用了 init 方法之后,这个对象才真正能使用。
到此为止一个对象就产生了
53,双亲委托模型
双亲委托模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需要加载的类)时,子加载器才会尝试自己去加载。
使用双亲委托机制的好处是:能够有效确保一个类的全局唯一性,当程序中出现多个限定名相同的类时,类加载器在执行加载时,始终只会加载其中的某一个类。
54,非关系型数据库有哪些,优缺点
MongoDB、ElasticSearch、Redis
Mongodb
(1)Mongodb的不足之处
1、在集群分片中的数据分布不均匀
2、单机可靠性比较差
3、大数据量持续插入,写入性能有较大波动
4、磁盘空间占用比较大
(2)Mongodb的过人之处
1、无模式
2、查询与索引方式灵活,是最像SQL的Nosql
3、支持复制集、主备、互为主备、自动分片等特性
redis
优点:
1读写性能优异
2支持数据持久化,支持AOF和RDB两种持久化方式
3支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
4数据结构丰富:除了支持string类型的value外还支持string、hash、set、sortedset、list等数据结构。
缺点:
1不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
2主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
3的主从复制采用全量复制,复制过程中主机会fork出一个子进程对内存做一份快照,并将子进程的内存快照保存为文件发送给从机,这一过程需要确保主机有足够多的空余内存。若快照文件较大,对集群的服务能力会产生较大的影响,而且复制过程是在从机新加入集群或者从机和主机网络断开重连时都会进行,也就是网络波动都会造成主机和从机间的一次全量的数据复制,这对实际的系统运营造成了不小的麻烦。
4Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
55,Tomcat请求过程
模拟一下Tomcat处理一个Http请求的过程:设置一个来自客户端URL:http://localhost:8080/webgateway/index
① 服务器8080端口接收到客户端发来的请求,被一个在那里监听的叫HTTP1.1的Connector获取了这个链接请求;
② Connector把请求交给同在Service下的Engine去处理,并等待Engine的响应;
③ Engine把url解析,并把请求传给相对应的Host处理,如果没有相对应的Host,则用默认名叫localhost的Host来处理;
④ Host再把url解析为/webgateway/index.html,匹配context-path为/webgateway的Context去处理(如果匹配不到就把该请求交给路径名为””的Context去处理);
⑤ context-path为/webgateway的Context会匹配Servlet Mapping为/index的Servlet处理;
⑥ 构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用Servlet的doGet或doPost方法;
⑦ Context把处理完的HttpServletResponse对象返回给Host;
⑧ Host把HttpServletResponse对象返回给Engine;
⑨ Engine把HttpServletResponse对象返回给Connector;
⑩ Connector把HttpServletResponse对象返回给客户browser。
56,服务器加载web.xml过程
1,当启动一个WEB项目时,容器包括(JBoss、Tomcat等)首先会读取项目web.xml配置文件里的配置,当这一步骤没有出错并且完成之后,项目才能正常地被启动起来。
2,启动WEB项目的时候,容器首先会去它的配置文件web.xml读取两个节点:
3,紧接着,容器创建一个ServletContext(application),这个web项目的所有部分都将共享这个上下文。容器以
4,容器创建
得到这个context-param的值之后,你就可以做一些操作了。
举例:你可能想在项目启动之前就打开数据库,那么这里就可以在
4,接着,容器会读取
5,以上都是在WEB项目还没有完全启动起来的时候就已经完成了的工作。如果系统中有Servlet,则Servlet是在第一次发起请求的时候被实例化的,而且一般不会被容器销毁,它可以服务于多个用户的请求。所以,Servlet的初始化都要比上面提到的那几个要迟。总的来说,web.xml的加载顺序是:
6,对于某类元素而言,与它们出现的顺序是有关的。以
57,cookie和session区别,分布式环境下如何保存用户状态
1、session保存在服务器,客户端不知道其中的信息;cookie保存在客户端,服务器能够知道其中的信息。
2、session中保存的是对象,cookie中保存的是字符串。
3、session不能区分路径,同一个用户在访问一个网站期间,所有的session在任何一个地方都可以访问到。而cookie中如果设置了路径参数,那么同一个网站中不同路径下的cookie互相是访问不到的。
4、session需要借助cookie才能正常。如果客户端完全禁止cookie,session将失效。
分布式Session的几种实现方式
1 .基于数据库的Session共享
2 .基于NFS共享文件系统
3 .基于memcached 的session,如何保证 memcached 本身的高可用性?
4 . 基于resin/tomcat web容器本身的session复制机制
5 . 基于TT/Redis 或 jbosscache 进行 session 共享。
6 . 基于cookie 进行session共享
58,Spring 框架中都用到了哪些设计模式?
Spring 框架中使用到了大量的设计模式,下面列举了比较有代表性的:
代理模式 — 在 AOP 和 remoting 中被用的比较多。
单例模式 — 在 Spring 配置文件中定义的 Bean 默认为单例模式。
模板方法 — 用来解决代码重复的问题。比如 RestTemplate、JmsTemplate、JdbcTemplate 。
前端控制器 — Spring提供了 DispatcherServlet 来对请求进行分发。
视图帮助(View Helper) — Spring 提供了一系列的 JSP 标签,高效宏来辅助将分散的代码整合在视图里。
依赖注入 — 贯穿于 BeanFactory / ApplicationContext 接口的核心理念。
工厂模式 — BeanFactory 用来创建对象的实例。
59,Spring中Bean的生命周期
实例化bean对象(通过构造方法或者工厂方法)
设置对象属性(setter等)(依赖注入)
如果Bean实现了BeanNameAware接口,工厂调用Bean的setBeanName()方法传递Bean的ID。(和下面的一条均属于检查Aware接口)
如果Bean实现了BeanFactoryAware接口,工厂调用setBeanFactory()方法传入工厂自身
将Bean实例传递给Bean的前置处理器的postProcessBeforeInitialization(Object bean, String beanname)方法
调用Bean的初始化方法
将Bean实例传递给Bean的后置处理器的postProcessAfterInitialization(Object bean, String beanname)方法
使用Bean
容器关闭之前,调用Bean的销毁方法
60,Spring 中有多少种 IoC 容器?
Spring 提供了两种( 不是“个” ) IoC 容器,分别是 BeanFactory、ApplicationContext 。
BeanFactory
BeanFactory 在 spring-beans 项目提供。
BeanFactory ,就像一个包含 Bean 集合的工厂类。它会在客户端要求时实例化 Bean 对象。
ApplicationContext
ApplicationContext 在 spring-context 项目提供。
ApplicationContext 接口扩展了 BeanFactory 接口,它在 BeanFactory 基础上提供了一些额外的功能。内置如下功能:
MessageSource :管理 message ,实现国际化等功能。
ApplicationEventPublisher :事件发布。
ResourcePatternResolver :多资源加载。
EnvironmentCapable :系统 Environment(profile + Properties)相关。
Lifecycle :管理生命周期。
Closable :关闭,释放资源
InitializingBean:自定义初始化。
BeanNameAware:设置 beanName 的 Aware 接口。
另外,ApplicationContext 会自动初始化非懒加载的 Bean 对象们。
61,Lock与synchronized有以下区别:
Lock是一个接口,而synchronized是关键字。
synchronized会自动释放锁,而Lock必须手动释放锁。
Lock可以让等待锁的线程响应中断,而synchronized不会,线程会一直等待下去。
通过Lock可以知道线程有没有拿到锁,而synchronized不能。
Lock能提高多个线程读操作的效率。
synchronized能锁住类、方法和代码块,而Lock是块范围内的
62,abstract不可以和哪些关键字共存
private:abstract是表明希望被覆盖的,但是private,子类看不到。
static:如果是static就需要创建对象,但abstract不需要创建对象(没有意义)
final:final表示不能被子类覆盖,abstract表名希望被覆盖
63,Mybatis工作流程
(1)加载配置并初始化
触发条件:加载配置文件
配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
(2)接收调用请求
触发条件:调用Mybatis提供的API
传入参数:为SQL的ID和传入参数对象
处理过程:将请求传递给下层的请求处理层进行处理。
(3)处理操作请求 触发条件:API接口层传递请求过来
传入参数:为SQL的ID和传入参数对象
处理过程:
(A)根据SQL的ID查找对应的MappedStatement对象。
(B)根据传入参数对象解析MappedStatement对象,得到最终要执行的SQL和执行传入参数。
(C)获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。
(D)根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。
(E)释放连接资源。
(4)返回处理结果将最终的处理结果返回。
orm工具的基本思想
无论是用过的hibernate,mybatis,都可以发现他们有一个共同点:
从配置文件(通常是XML配置文件中)得到 sessionfactory.
由sessionfactory 产生 session
在session 中完成对数据的增删改查和事务提交等.
在用完之后关闭session 。
简化:
工作原理
1 使用连接池
2 统一sql 存取xml
3 参数封装和结果映射
4 sql语句的复用封装
执行流程
1 页面发送请求到web.xml
2 由springMvc去找handler
3 前端控制器调用适配器执行handler
4 视图渲染展示
64,如何避免消息重复投递或重复消费?
在消息生产时,MQ 内部针对每条生产者发送的消息生成一个 inner-msg-id ,作为去重和幂等的依据(消息投递失败并重传),避免重复的消息进入队列
在消息消费时,要求消息体中必须要有一个 bizId(对于同一业务全局唯一,如支付 ID、订单 ID、帖子 ID 等)作为去重和幂等的依据,避免同一条消息被重复消费。
65,String,StringBuilder以及StringBuffer这三个类之间有什么区别
执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String
在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况