java 最新面试宝典

一、基础

##### 1、Java都有哪些数据类型?基本数据类型有哪些?分别占多少字节?多少位?引用数据类型又有哪些?

- 基本数据类型:byte(1)、short(2)、int(4)、long(8)、float(4)、double(8)、char(2)、boolean(1)

- 引用数据类型:数组、集合、对象

##### 2、Java语言的几大特性是什么?分别怎么理解?(封装、继承、多态的好处)

- 封装:就是把对象的属性和行为(数据)结合为一个独立的整体,并尽可能隐藏对象的内部实现细节,就是把不想告诉或者不该告诉别人的东西隐藏起来,把可以告诉别人的 公开,别人只能用我提供的功能实现需求,而不知道是如何实现的。增加安全

- 继承:子类继承父类所有非私有的数据属性和行为,并能根据自己的需求扩展出新的行为,提高了代码的复用性

- 多态:指允许不同的对象对同一消息做出相应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式,封装和继承几乎都是为多态而准备的

##### 3、Java的权限修饰符有哪些?都能加在哪些地方?分别代表什么意义?

- 4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类,public和默认可以修饰类

- public:全局访问

- 默认:不写修饰符,只能同包中访问

- protected:同包和不同包的子类访问

- private:只有同类能访问

##### 4、什么是重写?什么是重载?

重写:父子两类中,子类重写父类方法,方法名和参数列表相同,子类抛异常和返回值的范围小于等于父类,权限修饰符大于等于父类

重载:同一类中,方法名相同,参数列表不同,返回值,权限修饰符和抛异常可以不一样

##### 5、final关键字能加在哪些地方?分别代表什么?

类:不可被继承

方法:不可被重写,能重载

变量:基本类型:初始化后值不能改变

​引用类型:初始化后地址值不能改变

##### 6、static关键字能加在哪些地方?分别代表什么?

方法:静态方法,不依赖于任何对象就可以进行访问

成员变量:静态成员变量,存在方法区,别所有线程共享,属于该类的所有对象

代码块:静态代码块,是当Java类加载到JVM内存中而执行的代码块,由于类的加载在JVM运行期间只会发生一次,所以静态代码块也只会执行一次。

内部类:不依赖于外部类实例对象而被实例化;不能访问外部类的普遍成员变量;只能访问外部类中的静态成员和静态方法。

##### 7、接口中可以有哪些成员?抽象类呢?接口和抽象类又有什么区别?(注意JDK1.8接口中是可以出现非抽象方法的:default方法、静态方法)

接口:方法,变量

抽象类:方法,变量,构造器

区别:1.接口没有构造器,抽象类有

​2.接口的变量默认被public static final修饰,都是静态常量,抽象类有静态常量也有变量

​3.接口有抽象方法,JDK1.8新增静态方法和默认方法,JDK1.9新增私有方法

##### 8、Java异常体系是什么?(编译)异常有什么区别?常见的运行时异常有哪些?

顶层父类:Throwable:包括Error和Exception

Error:描述了Java运行时系统的内部错误和资源耗尽错误。一般是指虚拟机(JVM)相关的问题,如系统崩溃,虚拟机出错误等,**这种错误无法恢复或不可能捕获,将导致应用程序中断**,通常不处理。因为如果出现这样的内部错误,除了通告用户,并尽力使程序安全地终止之外,再也无能为力了。

Exception:**checked Exception**(编译时异常也叫非运行时异常)和 **RuntimeException**(运行时异常)

- RuntimeException:异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生

- 编译时异常:是RuntimeException以外的异常,从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等

常见的运行时异常:

ArithmeticException 被除数不能为0的异常

NullPointerException 空指针异常

ArrayIndexOutOfBoundsException 数组下标越界异常

ClassCastException 类型转换异常

ArrayStoreException 尝试将一个不同的数据类型存放到数组中

IndexOutOfBoundsException 尝试从容器中获取一个没有的数据

DateTimeException LocalDateTime会抛出这个异常

NegativeArraySizeException 创建一个负数的数组

NumberFormatException 尝试将一个错误的字符串格式转换为数字 必须为数字格式的字符串

IllegalArgumentException 代码格式参数传递错误

##### 9、== 和 equals的异同?

==是运算符,equals是方法

==:基本数据类型:比较值

​ 引用数据类型:比较地址值

equals:Object中的方法,比较的是地址值,String,Integer,Data等类重写了equals方法,比较的是内容

##### 10、&与&&、|与||的区别?

&运算符有两种用法:(1)按位与;(2)逻辑与。

&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true 整个表达式的值才是 true。

&&之所以称为短路运算是因为,如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是 null 而且不是空字符串,应当写为 username != null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的 equals 比较,否则会产生 NullPointerException 异常。

逻辑或运算符(|) 和短路或运算符(||)的差别也是如此。

##### 11、String可以修改本身吗?为什么?

不可以

在String类的底层源码,String类中有一个char数组是用于存储字符串的,并且这个char数组是用final修饰的,而且我们都知道数组一旦创建它的长度就是不可变的,并且被final修饰的引用类型一旦被赋值,也就是说指向某个对象之后,是不可以再修改它指向的空间的,所以String不可变的

##### 12、StringBuffer和StringBuilder的区别是什么?

AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了 一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。

StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。效率低

StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的 效率高

##### 13、valueOf和toString的区别?

1.toString()返回的是字符串,而valueOf()返回的是原始值,没有原始值返回对象本身

2.undefined和null都没有toString()和valueOf()方法

3.Date类型的toString()返回的表示时间的字符串;valueOf()返回的是现在到1970年1月1日的毫秒数

4.Number类型的toString()方法可以接收转换基数,返回不同进制的字符串形式的数值;而valueOf()方法无法接受转换基数

5.Object.prototype.toString.toString()能够很好的判断数据的类型及内置对象

toString(): 是返回一个反映这个对象的字符串

valueOf(): 是返回它相应的原始值

valueOf偏向于运算,toString偏向于显示。在进行对象转换时,将优先调用toString方法,在进行强转字符串类型时,将优先调用 toString 方法,转为数字时优先调用 valueOf。使用运算操作符的情况下,valueOf的优先级高于toString。”

##### 14、大量字符串用 "+" 号进行拼接效率高吗?为什么?应该用什么替代?为什么?

不高

少量数据使用是可以的 大量数据使用+进行字符串的拼接,每一次拼接生成的都是新的字符串,都需要再次占用空间,所以对于大量数据用+进行String的拼接消耗很大,而append方法是提前申请一个较大的空间,之后进行String的拼接可以直接接在后面,不生成新的字符串对象,如果该空间用完了,则再申请双倍的空间进行拼接。成倍增加空间的时候要重新申请空间,然后把原来数据复制到新的空间。

##### 15、创建一个类的实例都有哪些办法?

方法:1.使用new关键字

​2.利用java的反射机制,调用Java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法。

​3.类实现克隆接口,调用对象的clone()方法

​4.运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法

##### 16、Java集合的体系是什么样的?

顶层接口:Collection:分为单列集合(List)和双列集合(Map)

List

- List:有序,可重复

​1.ArrayList :

​优点: 底层数据结构是数组,查询快,增删慢。

​缺点: 线程不安全,效率高

​2.Vector

​优点: 底层数据结构是数组,查询快,增删慢。

​缺点: 线程安全,效率低, 已给舍弃了

​3.LinkedList

​优点: 底层数据结构是链表,查询慢,增删快。

​缺点: 线程不安全,效率高

- Set:无序,唯一

​1.HashSet 底层数据结构是哈希表。(无序,唯一)

​ 如何来保证元素唯一性? 依赖两个方法:hashCode()和 equals()

​2.LinkedHashSet 底层数据结构是链表和哈希表。

​(FIFO 插入有序,唯一) 1.由链表保证元素有序 2.由哈希表保证元素唯一

​3.TreeSet 底层数据结构是红黑树。(唯一,有序)

​如何保证元素排序的呢? 自然排序 比较器排序

​如何保证元素唯一性的呢? 根据比较的返回值是否是 0 来决定

Map

- HashMap

- 基于 hash 表的 Map 接口实现,非线程安全,高效,支持 null 值和 null 键, 线程 不安全。

- HashTab

- 线程安全,低效,不支持 null 值和 null 键

- LinkedHashMap

- 线程不安全,是 HashMap 的一个子类,保存了记录的插入顺序;

- TreeMap

- 能够把它保存的记录根据键排序,默认是键值的升序排序,线程不安全。

- WeakHashMap

- 继承WeakReference,无论内存充足与否,都会被垃圾收集回收

##### 17、Set和List分别有哪些特点?Set去重的原理?

- Set:存取无序,不可重复

- List:存取有序,可重复

- Set去重原理:

- TreeSet:通过实现Comparable接口并重写CompareTo方法实现自定义去重。CompareTo方法的返回值为0的情况下被视为相同元素进行去重

- HashSet:HashSet底层是用HashMap存储数据的。当向HashSet中添加元素的时候,首先计算元素的hashcode值,然后通过扰动计算和按位与的方式计算出这个元素的存储位置,如果这个位置位空,就将元素添加进去;如果不为空,则用equals方法比较元素是否相等,相等就不添加,否则找一个空位添加。

##### 18、ArrayList底层原理是什么?扩容原理?

- 底层原理:

- ArrayList底层数据结构是数组,当创建ArrayList对象时,底层初始化了一个空数组,数组是Object类型,数组名是elementData

- 扩容原理:

- 当第一次添加元素时,数组长度扩容为10

当第11次添加时,会触发扩容机制,其实就是调用 grow方法,扩容为原数组长度的1.5倍。

每次扩容时,都是创建一个新数组,将老数组的元素通过 Arrays工具类复制到新数组中。elementData 指向了新数组

##### 19、LinkedList底层原理是什么?和ArrayList的区别是什么?

- 底层原理

- LinkedList是一个实现了List接口和Deque接口的双端链表。 LinkedList底层的链表结构使它支持高效的插入和删除操作,但是要查询的话只能遍历查询,因此查询的效率低下,另外它实现了Deque接口,使得LinkedList类也具有队列的特性; LinkedList不是线程安全的,如果想使LinkedList变成线程安全的,可以调用静态类Collections类中的synchronizedList方法

-

- LinkedList 底层数据结构为双向链表,链表结构,基于一个个链表节点Node,节点储存的是当前块数据与下一块地址

- 和ArrayList的区别

- Arraylist底层存储数据使用数组形式进行数据存储,可以通过下标快速获取指定数据的值,查找修改快,增删慢

- linkedList底层以链表形式进行数据存储,每一块只存储当前块数据与下一块地址,添加与删除操作时较快,查找和修改慢

##### 20、HashMap的底层原理是什么?map添加数据put的过程?扩容原理?

- 底层原理:

- HashMap 在 JDK1.8 之前的实现方式 数组+链表, 但是在 JDK1.8 后对 HashMap 进行了底层优化,改为了由 数组+链表或者数值+红黑树 实现,主要的目的是提高查找效率

- Jdk8 数组+链表或者数组+红黑树实现,当链表中的元素超过了 8 个以后并且数组长度超过64, 会将链表转换为红黑树,扩容时当红黑树节点小于等于6 时又会退化为链表

- put过程:

- 首先将 k,v 封装到 Node 对象当中(节点)。

- 先调用 k 的 hashCode()方法得出哈希值,并通过哈希算法转换成数组的下标。

- 下标位置上如果没有任何元素,就把 Node 添加到这个位置上。如果说下标对应的位 置上有链表。此时,就会拿着 k 和链表上每个节点的 k 进行 equal。如果所有的 equals 方 法返回都是 false,那么这个新的节点将被添加到链表的末尾。如其中有一个 equals 返回了 true,那么这个节点的 value 将会被覆盖

- 扩容原理:

- 当 new HashMap():底层没有创建数组,首次调用 put()方法示时,底层创建长度为16,加载因子为0.75的数组,jdk8 底层的数组是:Node[],而非 Entry[],用数组容量大小乘以加载因子得到一个值,一旦数组中存储的元素个数超过该值就会调用 rehash 方法将数组容量增加到原 来的两倍,专业术语叫做扩容,在做扩容的时候会生成一个新的数组,原来的所有数据需要 重新计算哈希码值重新分配到新的数组

##### 21、concurrentHashMap原理是什么?

底层原理:

- JDK1.7

- Segments分段锁+HashEntry数组+链表,采用**分段锁**保证安全性

- 在进行数据的定位时,会首先找到 `segment`, 然后在 `segment` 中定位 `bucket`。如果多线程操作同一个 `segment`, 就会触发 `segment` 的锁 `ReentrantLock`, 这就是分段锁的**基本实现原理**

- 一个ConcurrentHashMap中有一个Segments数组,一个Segments中存储一个HashEntry数组,每个HashEntry是一个链表结构的元素。

segment继承自ReentrantLock锁。 首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一段数据时,其他段的数据也能被其他线程访问,实现了真正的并发访问。

可以通过构造函数指定,数组扩容不会影响其他的segment,get无需加锁,volatile保证内存可见性

- JDK1.8:

- JDK8中ConcurrentHashMap参考了JDK8 HashMap的实现,采用了数组+链表+红黑树的实现方式来设计,ConcurrentHashMap增加了同步的操作 CAS+Synchronized保证线程安全

- ​

##### 22、JDK8对于HashMap做了哪些优化?

```xml

1、Hash算法优化用 (key的hash值) 与 (key的hash值右移16位)进行异或运算

2、寻址算法优化

核心:hash & (n-1)不再像JDK1.7一样进行取模运算,取模运算相对来说性能比较差一些,hash&(n-1) 与取模操作效果一样,但是与运算的性能更高,数学上得到结论,当数组长度是2的幂次时,hash值对数组长度取模的效果和 hash&(n-1) 是一样的。

3、 解决hash碰撞问题

数组加链表-------> 数组+链表/数组+红黑树

4. jdk1.7中扩容后死循环问题(头插法改用尾插法)

```

##### 23、什么是socket?什么是IO/NIO/BIO/AIO?区别是什么?

Socket:是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。

BIO:同步阻塞IO。服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器需要启动一个线程进行处理,如果这个链接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

NIO:同步非阻塞IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这需要用户进行不停的去询问。NIO的包括三个核心概念:缓冲区(Buffer)、通道(Channel)、选择器(Selector)。

AIO:Asynchronous IO,异步非阻塞AIO。最大的特性时具有异步能力,这种能力对socket与文件I/O都起作用。AIO其实是一种在读写操作结束之前允许进行其他操作的I/O处理。

区别:

- BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简 单使用方便,并发处理能力低。

- NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。

- AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO , 异步 IO 的操作基于事件和回调机制

##### 24、什么是反射?可以用来干嘛?列举一下反射应用场景?什么是暴力反射?

- 反射:就是程序运行状态中,对于任意一个类,能够知道这个类的所有属性和方法,对于任意一个对象,能够调用方法/获取属性。

- 获取类的所有信息,调用对象方法。1、获取成员变量2、获取构造方法3、获取成员方法4、获取类名

- 应用场景:

1、jdbc中连接数据库时候,反射加载驱动程序

2、Spring中 IOC容器根据配置文件信息 --> 获取类文件信息 --> 反射创建bean实例,并反射调用Set方法设置属性

3、SpringMVC自动识别前台传递过来的参数,自动组装成JavaBean,这也是通过反射来实现的

- 暴力反射:反射里的Constructor,Field,Method三个类都有一个getDeclaredXxx方法,可以不受权限控制的获取类的构造函数,字段,方法,如果想要私有构造函数创建对象,字段赋值,方法调用的话,会自动的访问类的isAccessable,默认的是false,所以,你想要访问类中的私有成员的时候,就要调用setAccessable()方法,将其改为true,这样,你就可以对类中的私有成员进行操作了.

##### 25、算法了解过吗?冒泡排序、选择排序、快排原理?

- 冒泡排序:每一轮两两之间相互比较,将更大者移至右方,则每一轮结束,当前最大的数一定被移到了最右边。

- 选择排序的思想与冒泡排序看起来很类似,但是实际上是不同的,要注意区分。

选择排序的思想是,对 n n n 个数而言,我们每次从未排序元素中暴力直接选出当前的最小数,然后移到序列的最前面。

- 快排:快速排序的主要思想是通过划分将待排序的序列分成前后两部分,其中前一部分的数据都比后一部分的数据要小,然后再递归调用函数对两部分的序列分别进行快速排序,以此使整个序列达到有序。

##### 26、JDK1.8的新特性有哪些?(lamda表达式、stream流、函数式接口、接口中默认方法、方法引用等等)

- 接口中新增了默认方法和静态方法

- JDK8内置Base64编码

- 加入了时间类,LocalDateTime,LocalDate,LocalTime

- Stream流Api:把真正的函数式编程风格引入到 Java 中。这种风格将要处理的元素集 合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选, 排序,聚合等

- 函数式接口:有且仅有一个抽象方法的接口叫做函数式接口,函数式接口可以被隐式转换为 Lambda 表达式

- Lambda 表达式: 允许把函数作为一个方法的参数。

- 方法引用:方法引用允许直接引用已有 Java 类或对象的方法或构造方法

- Optional类:Optional 类是一个可以为 null 的容器对象。如果值存在则 isPresent()方法会返 回 true,调用 get()方法会返回该对象。

##### 27、IO流体系

##### 28、如何实现分布式主键自增?

a、 利用中间件MyCat:

作为一个分布式数据库中间,屏蔽了数据库集群的操作,让我们操作数据库集群就像操作单机版数据库一样,对于主键自增,它有自己的方案:

通过本地文件 、实现

通过数据库实现

通过本地时间戳实现

通过分布式 ZK ID 生成器实现

通过 ZK 递增方式实现

b、在此提供一个分布式的主键生成方案:

1.使用独立的一张表保存,如ids来保存最小未被使用的id值,该表每条记录对应一个业务表的id使用,该表有两个字段key, value, key对应于每个业务表,value是相应的id值

2.使用两个变量来辅助ID的生成,nextId:下一个Id的值, maxId:当前允许的最大Id,当有生成Id请求时,首先判断 nextId > maxId,若条件成立,则重新执行2中的步骤从ids表中读取并更新value,然后将value赋值给nextId,将maxId赋值为value+99,将nextId加一,并返回旧的nextId的值。(注:增加nextId值的时候需要进行线程同步)

## 二、JVM

##### 1、JDK和JRE的区别是什么?

- 区别:

- JDK是开发工具包,主要面向开发人员。JDK包含了运行环境JRE,一些基本类库和开发工具

- JRE是Java程序的运行环境,面向Java程序的使用者,而不是开发者。JRE包括Java虚拟机(jvm)、Java核心类库和支持文件

##### 2、JVM的内存模型描述下?

1、Class Loader 类加载器 负责加载class文件,class文件在文件开头有特定的文件标示,并且ClassLoader只负责class文件的加 载,至于它 是否可以运行,则由Execution Engine决定。

2、Execution Engine 执行引擎负责解释命令,提交操作系统执行

3、Native Interface 本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,Java 诞生的时候是 C/C++横行的 时候,要想立足,必须有调用C/C++程序,于是就在内存中专门开辟了一块区域处理标记为native的代 码,它的具 体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载native libraies。目 前该方法使用的越来越少了,除非是与硬件有关的应用,比如通过Java程序驱动打印机,或者Java系统 管理生产设备,在企业级应用中已经比较少见。因为现在的异构领域间的通信很发达,比如可以使用 Socket通信,也可以使用Web Service等等, 不多做介绍。

4、Method Area方法区 方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定 义。简单 说,所有定义的方法的信息都保存在该区域,此区属于共享区间。 静态变量+常量+类信息+运行时常量池(JDK1.6)存在方法区中 实例变量存在堆内存中保存。

5、PC Register 程序计数器 每个线程都有一个程序计数器,就是一个指针,指向方法区中的方法字节码(下一个将要执行的指令代 码),由执行 引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记。

6、Native Method Stack 本地方法栈 它的具体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载native libraies。

7、 java栈 存放java的方法

8、堆:实例变量存在堆内存中保存

##### 3、JVM双亲委派加载机制,为什么JVM这么做?有违反双亲委派的例子吗?

* **1** **工作流程**

1.当Application ClassLoader 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个

请求委派给父类加载器Extension ClassLoader去完成。

2.当Extension ClassLoader收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求

委派给父类加载器Bootstrap ClassLoader去完成。

3.如果Bootstrap ClassLoader加载失败(在<JAVA_HOME>\lib中未找到所需类),就会让Extension

ClassLoader尝试加载。

4.如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载。

5.如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载。

6.如果均加载失败,就会抛出ClassNotFoundException异常。

“**双亲委派 ”机制只是Java推荐的机制,并不是强制的机制。**

**我们可以继承java.lang.ClassLoader类,实现自己的类加载器。如果想保持双亲委派模型,就应该重**

**写findClass(name)方法;如果想破坏双亲委派模型,可以重写loadClass(name方法。**

- 因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。 考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来 动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种 情况,因为String已经在启动时被加载,所以用户自定义类是无法加载。

- 违反双亲委派的例子:

- 第一次被打破是在Java的原始版本,那时候用户自定义类加载器已经存在,双亲委派机制为了兼容这些代码,但又无法保证loadClass不被子类重写,所以提供了findClass的方法。用户加载类的时候就去重写这个方法。如此一来,类加载的时候还是会调用加载器的loadClass向上请求,只有当父类加载器请求失败的时候,才会回来调用该类加载器被用户重写的findClass方法。

- 第二次打破则是由于JNDI服务(JDBC/JCE/JAXB/JBI),JNDI的目的就是对资源进行查找和集中管理,该类由启动类加载器去加载,但是却需要调用其他厂商部署在类路径下的JNDI服务提供者接口,由于父亲不认识儿子,启动类加载器是不认识这些接口的,那怎么办呢?

线程上下文类加载器:提供父类加载器访问子类加载器的行为。

- 第三次打破是热部署、热替换引起的。

Java热部署模块的规范化模块是OSGi提供的,热部署实现的关键就是OSGi自定义了类加载器,它为每个模块都配了一个类加载器。当需要动态地更换一个模块的时候,就把模块连通这个模块的类加载器一起替换,从而实现了热替换。

这种情况下,类加载器再也不是树状结构了,而是网状

##### 4、类的加载流程是什么样的,每个阶段解释一下

① 加载 这个很简单,程序运行之前jvm会把编译完成的.class二进制文件加载到内存,供程序使用,用到的就是 类加载器classLoader ,这里也可以看出java程序的运行并不是直接依靠底层的操作系统,而是基于jvm 虚拟机。如果没有类加载器,java文件就只是磁盘中的一个普通文件。

② 连接 连接是很重要的一步,过程比较复杂,分为三步 验证 》准备 》解析

- 验证:确保类加载的正确性。一般情况由javac编译的class文件是不会有问题的,但是可能有人的 class文件是自己通过其他方式编译出来的,这就很有可能不符合jvm的编译规则,这一步就是要过滤掉 这部分不合法文件

- 准备:为类的静态变量分配内存,将其初始化为默认值 。我们都知道静态变量是可以不用我们手动 赋值的,它自然会有一个初始值,比如int 类型的初始值就是0 ;boolean类型初始值为false;引用类型 的初始值为null 。 这里注意,只是为静态变量分配内存,此时是没有对象实例的

- 解析:把类中的符号引用转化为直接引用。解释一下符号引用和直接引用。比如在方法A中使用方 法B, 这里的B()就是符号引用,初学java时我们都是知道这是java的引用,以为B指向B方法的内存地址, 但是这是不完整的,这里的B只是一个符号引用,它对于方法的调用没有太多的实际意义,可以这么认 为,他就是给程序员看的一个标志,让程序员知道,这个方法可以这么调用,但是B方法实际调用时是 通过一个指针指向B方法的内存地址,这个指针才是真正负责方法调用,他就是直接引用。

③ 初始化 为类的静态变量赋予正确的初始值,上述的准备阶段为静态变量赋予的是虚拟机默认的初始值,此处赋 予的才是程序编写者为变量分配的真正的初始值

##### 5、JVM的GC的主要区域以及各自的GC机制是什么样的?

- 主要区域:老年代和年轻代

- 年轻代:

- 新生代GC之Serial收集器

- Serial 收集器是一个单线程的收集器,但它的“单线程”的意义并不仅说明它 只会使用单个CPU 或单条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,**必须暂停其 他所有的工作线程**,直到它收集结束。StopThe World"这个名字也许听起来很酷,但这项工作实际上是由 虚拟机在后台自动发起和自动完成的,在用户不可见的情况下把用户正常工作的线程全部停掉,这对很 多应用来说都是难以接受的

- 对应的JVM参数:-XX:+UseSerialGC

- 开启后会使用Serial(Young区用)+Serial Old(Old区用)的收集器组合

- 表示:新生代老年代都会使用串行回收收集器,新生代使用复制算法,老年代使用标记-整理算法

- 新生代GC之ParNew收集器

- ParNew垃圾收集器是Serial收集器的多线程版本 ,会STW暂停其他所有的工作线程直到他收集结束

- JVM参数:-XX:+UseParNewGC

- 启用ParNew收集器,只影响新生代的收集,不影响老年代

- 开启后:young区:ParNew + old区:Serialold

- 新生代使用复制算法,老年代使用标记-整理算法

- -XX:ParallelGCThreads:指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同

- 新生代GC之Parallel收集器

- 类似于ParNew,也是一个新生代垃圾收集器,使用复制算法,也是一个并行的多线程的垃圾收集器,俗称吞吐量优先收集器,串行收集器在新生代和老年代的并行化,重点关注的是:可控制的吞吐量,高吞吐量意味着高效利用CPU的时间,它多用于在后台运算而不需要太多交互的任务。

- 吞吐量 = 程序运行的时间/(程序运行的时间+垃圾回收的时间)

- 与parNew收集器一个重要的区别是自适应调节策略:虚拟机会根据当前系统的运行情况,性能监控信息,动态调整这些参数以提供最合适的停顿时间,或最大的吞吐量

- 常用JVM参数: -XX:+UseParallelGC或-XX:+UseParallelOldGC(可互相激活)使用 Parallel收集器

- 开启改参数后:新生代复制算法,老年代标记-整理算法

- 老年代:

- 老年代ParallelOld收集器

- Parallel Old收集器是Parallel Scavenge的老年代版本,使用多线程的标记-整理算法,Parallelold收集 器在JDK1.6才开始提供。 在JDK1.6之前,新生代使用ParallelScavenge收集器只能搭配年老代的SerialOld收集器,只能保证新生代的吞吐量优先,无法保证整体的吞吐量,在JDK1,6之前(Parallel Scavenge + serial old) Parallel old正是为了在年老代同样提供吞吐量优先的垃圾收集器,如果系统对吞吐量要求比较高, JDK1,8后 可以优先考虑新生代Parallel Scavenge 和年老代Parallel old收集器的搭配策略,在JDK1,8及 后(Parallel Scavenge + Parallel old)

- JVM 参数: -XX:+UseParallelOldGC 使用ParallelOld收集器

- 设置改参数后,新生代Parallel Scavenge + Parallel old

- 老年代SerialOld收集器

- java8已经优化掉了 主要是做辅助的,防止CMS或者Parallel old挂掉 他能顶上去

- 老年代CMS收集器

- CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它而非常 符合在注重用户体验的应用上使用。非常适合堆内存大、cpu核数多的服务器端应用,也是G1出现之前 大型应用的首选收集器

- 初始标记 CMS initial mark:只是标记一下 GCRoot能直接关联的对象,速度很快,仍然需要暂停所有 的工作线程

- 并发标记 CMS concurrent mark:进行GCRoot的跟踪过程,和用户线程一起工作,不需要暂停工作线 程,主要标记过程,标记全部对象

- 重新标记 CMS remark:修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对 象的标记记录,仍然需要暂停所有的工作线程,为什么?由于并发标记,用户线程依然运行,因此在正 式清理前,再做修正

- 并发清除 CMS concurrent sweep:清除对象,和用户线程一起工作,不需要暂停工作线程,基于标记 结果,直接清理对象,由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户线程一起 并发工作,所以总体上来看,CMS收集器的内存回收和用户线程是一起并发执行

- 优点:并发收集,停顿低

- 缺点:1、并发执行,对CPU资源压力大2、采用的标记清除算法会导致大量碎片

- 由于并发进行,CMS收集与应用线程会同时增加对堆内存的使用,也就是说,CMS必须要在老年代堆内 存用完之前完成垃圾回收,否则CMS回收失败,将触发担保机制,串行老年代收集器将会以STW的方式 进行一次GC,从而造成较大停顿

- 开启收集器的JVM参数:-XX:+UseConcMarkSweepGC ,开启参数后,会自动将 -XX:+UseParNewGC 打 开 开启参数后:新生区: ParNew,养老区:CMS+SerialOld 收集器组合,SerialOld作为CMS出错的后备收集器

##### 6、JVM的GC算法都有哪些?

- ① 复制算法--年轻代的GC

- 复制整理算法的基本思想就是将内存分为两块,每次只用其中 一块,当这一块内存用完,就将还活着的对象复制到另外一块上面

```xml

在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的

紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。

经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。

不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重

复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中

优点:不会产生内存碎片。

缺点:

(1)会开辟新的空间也就是 To survivor,用来保存有用对象

(2)复制对象会花费一些时间

```

- ② 标记清除

- 分为标记和清除两阶段:首先标记出所有需要回收的对象,然后统一回收所有被标记的

- 缺点: 1、标记阶段和清除阶段的效率都不高。 2、显而易见的,清除后产生了大量不连续的内存碎片,导致在程序运行过程中需要分配较大对象的时 候,无法找到足够的连续内存而不得不提前触发一次垃圾收集动作。

- ③ 标记整理

- 标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的 对象都向一端移动,然后直接清理掉端边界以外的内存。

- 优点: 自带整理功能,这样不会产生大量不连续的内存空间,适合老年代的大对象存储。

- 算法小结:

- 内存效率:复制算法>标记清除算法>标记整理算法

- 内存整齐度:复制算法=标记整理算法>标记清除算法

- 内存利用率:标记整理算法=标记清除算法>复制算法

- ④ 分代收集

- 当前商业虚拟机的垃圾收集都采用分代收集。此算法没啥新鲜的,就是将上述三种算法整合了一下。具体如下:

- 根据各个年代的特点采取最适当的收集算法 :

- 1、在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法。只需要付出少量存活对象的复制成本就可以完成收集。

- 2、老年代中因为对象存活率高、没有额外空间对他进行分配担保,就必须用标记-清除或者标记-整理。

## 三、Spring & SpringBoot

##### 1、说下对SpringIOC的理解,怎么理解控制反转?

- IOC 的意思是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和 时机是由自己把控的,而现在这种权力转移到 Spring 容器中,并由容器根据配置文件去创 建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。最 直观的表达就是,IOC 让对象的创建不用去 new 了,可以由 spring 根据我们提供的配置文 件自动生产,我们需要对象的时候,直接从 Spring 容器中获取即可

##### 2、Spring如何解决IOC中的循环依赖问题?

- 什么是循环依赖:循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A依赖于B,B依赖于C,C⼜依赖于A

- 解决循环依赖的方法:

- 重新设计:当出现这种循环依赖时,很可能是在设计方面存在问题,没有把每个类的职责很好的区分开。应该尝试正确重新设计组件,以便其层次结构设计良好,并且不需要循环依赖项。‎

- 使用 setter/field 方法注入:只有构造方法是在上下文加载时就要求被注入,容易出现依赖循环。所以可以用其他的方式进行依赖注入,setter 和 field 方法注入与构造方法不同,它们不会在创Bean时就注入依赖,而是在被需要时才注入。

- 使用 @Lazy:解决Spring 循环依赖的一个简单方法就是对一个Bean使用延时加载。也就是说:这个Bean并没有完全的初始化完,实际上他注入的是一个代理,只有当他首次被使用的时候才会被完全的初始化。

- 使用 @PostConstruct:打破循环的另一种方式是,在要注入的属性(该属性是一个bean)上使用 @Autowired,并使用@PostConstruct 标注在另一个方法,且该方法里设置对其他的依赖。

- 实现ApplicationContextAware and InitializingBean接口:如果一个Bean实现了ApplicationContextAware,该Bean可以访问Spring上下文,并可以从那里获取到其他的bean。实现InitializingBean接口,表明这个bean在所有的属性设置完后做一些后置处理操作(调用的顺序为init-method后调用);在这种情况下,我们需要手动设置依赖。

##### 3、说下对SpringAOP的理解、有哪些通知?使用场景有哪些?(底层原理:两种动态代理)

- AOP的理解:一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关, 但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被 命名为“切面”(Aspect). SpringAOP 使用的动态代理,所谓的动态代理就是说 AOP 框 架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个 AOP 对象,这个 AOP 对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

- Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理:

- ① JDK 动态代理只提供接口代理,不支持类代理,核心 InvocationHandler 接口和 Proxy 类,InvocationHandler 通过 invoke()方法反射来调用目标类中的代码,动态地将 横切逻辑和业务编织在一起,Proxy 利用 InvocationHandler 动态创建一个符合某一接口 的的实例, 生成目标类的代理对象。

- ② 如果代理类没有实现 InvocationHandler 接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库, 可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从 而实现 AOP。CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final, 那么它是无法使用 CGLIB 做动态代理的。

- 通知类型:

- 前置通知[before]:在切点运行之前执行

- 后置通知[after-returning]:在切点正常结束之后执行

- 异常通知[after-throwing]:在切点发生异常的时候执行

- 最终通知[after]:在切点的最终执行

- 环绕通知[around]:环绕通知运行程序员以编码的方式自己定义通知的位置, 用于解决其他通知时序问题

- 使用场景

① 事务管理 (调用方法前开启事务, 调用方法后提交关闭事务 )

② 权限管理

```

情景1:控制用户的功能权限

方案详述:在@ControllerAdvice里边,处理全局请求,控制权限。

权限管理的其他方案:(除了AOP之外的方案)

在过滤器或者拦截器中处理

使用Shiro中间件

```

③ 异常处理

```

情景1:在@ControllerAdvice里边,处理全局异常

情景2:将Dubbo接口作为切面,统一处理Dubbo接口里边的异常

```

④ 操作日志

```

情景1:按产品的需求,有的接口需要记录操作日志

自定义注解,需要记录操作日志的,则在Controller的方法上加此注解

AOP中判断,如果有这个自定义注解,则将参数异步写到日志数据库

```

⑤ 将数据同步到ES

```

情景1:增删改数据时,同时要处理MySQL和ES

将相关类作为切面,若数据库提交,则写到ES;若回滚,则不写到ES

```

⑥ 事务控制

```

情景1:使用Spring的@Transactional

```

##### 4、说下SpringMvc的流程(从访问一个URL到得到页面结果的具体流程:DispatcherServlet的职责流程)

- ① 用户发送请求到前端控制器(DispatcherServlet)

- ② 前 端 控 制 器 ( DispatcherServlet ) 收 到 请 求 调 用 处 理 器 映 射 器 (HandlerMapping),返回HandlerExecutionChain执行链

- ③ 处理器映射器(HandlerMapping)找到具体的处理器(可以根据 xml 配置、注解进行 查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。

- ④ 前端控制器(DispatcherServlet)调用处理器映射器(HandlerMapping)

- ⑤ 处理器适配器(HandlerAdapter)去调用自定义的处理器类(Controller,也叫 后端控制器)。

- ⑥ 自定义的处理器类(Controller,也叫后端控制器)将得到的参数进行处理并返回ModeAndView给处理器映射器(HandlerMapping)

- ⑦ 处 理 器 适 配 器 ( HandlerAdapter ) 将 得 到 的 结 果 返 回 给 前 端 控 制 器 (DispatcherServlet)

- ⑧ DispatcherServlet( 前 端 控 制 器 ) 将 ModelAndView 传 给 视 图 解 析 器 (ViewReslover)

- ⑨ 视图解析器(ViewReslover)将得到的参数从逻辑视图转换为物理视图并返回给前端控制器

- ⑩ 前端控制器(DispatcherServlet)调用物理视图进行渲染并返回

- 11. 前端控制器(DispatcherServlet)将渲染后的结果返回

12. ​

第一步:用户发送请求到前端控制器(DispatcherService)。

第二步:前端控制器请求HandlerMapping 查找 Handler,可以根据 xml 配置、注解进行查找。

第三步:处理器映射器 HandlerMapping 向前端控制器返回Handler。

第四步:前端控制器调用处理器去执行 Handler。

第五步:处理器适配器执行 Handler。

第六步:Handler 执行完成后给适配器返回 Model And View。

第七步:处理器适配器向前端控制器返回 Model And View

Model And View 是springMVC 框架的一个底层对象包括 Model 和view

第八步:前端控制器请求视图解析器进行视图解析根据逻辑视图名来解析真正的视图。

第九步:视图解析器向前端控制器返回view。

第十步:前端控制器进行试图渲染 就是将模型数据(在Model And View 对象中)填充到request 域。

第十一步:前端控制器向用户响应结果。

##### 5、对Spring声明式事务的理解?Spring的事务隔离级别?Spring事务传播行为?

- spring声明式事务:所谓声明式事务,就是通过配置的方式,比如通过配置文件或者注解的方式,告诉spring,哪些方法需要spring帮忙管理事务,然后开发者只用关注业务代码,而事务的事情spring自动帮我们控制。

- ##### spring事务的事务隔离级别:

- ① ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别, 使用数据库默认的事务隔离级别。

- ② ISOLATION_READ_UNCOMMITTED:读未提交,允许另外一个事务可以看到这个 事务未提交的数据。

- ③ ISOLATION_READ_COMMITTED:读已提交,保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新。解决脏读问题

- ④ ISOLATION_REPEATABLE_READ:可重复读,保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新。行锁

- ⑤ ISOLATION_SERIALIZABLE:串行话;一个事务在执行的过程中完全看不到其他事务对数据库所做的更新。表锁

- ##### spring事务的传播行为:

- ① PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前 存在事务,就加入该事务,该设置是最常用的设置。

- ② PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务, 如果当前不存在事务,就以非事务执行。

- ③ PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事 务,如果当前不存在事务,就抛出异常。

- ④ PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建 新事务。

- ⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务, 就把当前事务挂起。

- ⑥ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

- ⑦ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前 没有事务,则按 REQUIRED 属性执行

##### 6、什么情况下会让spring事务失效

- 3①数据库引擎不支持事务,如果使用的数据库为MySQL,并且选用了MyISAM存储引擎,则Spring的事务就会失效,可以选择InnoDB

- ②事务方法未被Spring管理

- ③方法没有被public修饰,spring声明式事务是基于AOP,AOP的实现原理是动态代理,要通过代理的方式获取到代理的具体对象。如果方法无法重写,就无法被代理。所以static和final修饰方法也同样不能支持事务。

- ④异常类型错误,导致事务失败,因为默认回滚的是:RuntimeException,解决方法:1.指定支持的类型@Transactional(rollbackFor=Exception.class),2.抛出spring支持回滚的异常,try…cache(Exception e){Throw new RuntimeException(“运行时异常”)}

- ⑤catch异常后,没有再次抛出异常,导致事务失效,异常被吃了,不会正常回滚

- ⑥发生自调用,类里面使用this调用本类的方法(this通常省略),此时这个this对象不是代理类,而 是对象本身

- ⑦多线程调用,导致事务失效,方法调用时,两个方法不在同一个线程,获取的数据库连接不一样,从而是两个不同的事务。我们说的同一个事物,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和会滚。

- ⑧数据源没有配置事务管理器,导致事务失效

- ⑨传播类型不支持事务,导致事务失效,

- PROPAGATION_MANDATORY, 该级别的事务要求上下文中必须要存在事务,否则就会抛出异常

- PROPAGATION_NEVER,上下文不存在事务就抛异常

##### 7、Spring有哪些核心注解?分别的作用?

- @Component(任何层) @Controller @Service @Repository(dao): 用于 实例化对象

- @Scope : 设置 Spring 对象的作用域

- @PostConstruct @PreDestroy : 用于设置 Spring 创建对象在对象创建之后和销 毁之前要执行的方法

- @Value: 简单属性的依赖注入

- @Autowired: 对象属性的依赖注入

- @Qualifier: 要和@Autowired 联合使用,代表在按照类型匹配的基础上,再按照 名称匹配。

- @Resource 按照属性名称依赖注入 8. @ComponentScan: 组件扫描

- @Bean: 表在方法上,用于将方法的返回值对象放入容器

- @PropertySource: 用于引入其它的 properties 配置文件

- @Import: 在一个配置类中导入其它配置类的内容

- @Configuration: 被此注解标注的类,会被 Spring 认为是配置类。Spring 在启动 的时候会自动扫描并加载所有配置类,然后将配置 类中 bean 放入容器

- @Transactional 此注解可以标在类上,也可以表在方法上,表示当前类中的方法 具有事务管理功能

##### 8、Spring和SpringBoot的关系?

- 是 Spring 的子项目,主要简化 Spring 开发难度,去掉了繁重配置,提供各种启动器,可以 让程序员很快上手,节省开发时间

##### 9、SpringBoot的自动装配原理是什么?

- 自动装配的原理:

- 我们用的时候是在启动类上加@SpringBootApplication,这个注解是复合注解,内部包 含 @EnableAutoConfiguration

- @EnableAutoConfiguration 内部有一个@Import, 这个注解才是完成自动配置的关键.

- @Import 导入一个类(AutoConfigurationImportSelector),这个类内部提供了一个方 法(selectImports). 这个方法会扫描导入的所有 jar 包下的 spring.factories 文件. 解析文件中自动配置类 key=value, 将列表中的类创建,并放到 Spring 容器中

##### 10、SpringBoot的核心注解是哪个?详细说下

- 核心注解:@SpringBootApplication,复合注解,内部包含以下注解:

- 第一类: JDK 原生注解 4 个

```xml

@Target(ElementType.TYPE) //当前注解的使用范围

@Retention(RetentionPolicy.RUNTIME) //生命周期

@Documented //声明在生成 doc 文档时是否带着注解

@Inherited //声明是否子类会显示父类的注解

```

- 第二类: @SpringBootConfiguration 点开该注解源码, 会发现本质是@Configuration,定义该类是个配置类功能等同于 xml 配置文件

- 第三类: @ComponentScan, 包扫描功能. 这个注解对应 Spring 的 XML 配置中的@ComponentScan,其实就是自动扫描并加 载符合条件的组件(比如@Component 和@Repository 等)或者 bean 定义, 最终将这些 bean 定义加载到 IoC 容器中. 也可以通过 basePackages 等属性来细粒度的定制@ComponentScan 自动扫描的范围, 如果不指定, 则默认扫描@ComponentScan 所在类的 package 及子包进行扫描

- 第四类: @EnableAutoConfiguration 点开源码会发现,本质是@import, 自动导入功能

```xm

1. @EnableAutoConfiguration 也是借助@Import 的帮助,将所有符合自动配置条件的 bean 定义加载到 IoC 容器.

@EnableAutoConfiguration 会根据类路径中的 jar 依赖为项目进行自动配置, 如:添加了spring-boot-starter-web 依赖, 会自动添加 Tomcat 和 SpringMVC 的依赖,

SpringBoot会对 Tomcat 和 SpringMVC 进行自动配置.

2. 那么 SpringBoot 是如何完成自动配置的呢?

A. SpringBoot 自动配置的注解是 @EnableAutoConfiguration.

B. 我们用的时候是在启动类上加@SpringBootApplication,这个注解是复合注解,内部包

含 @EnableAutoConfiguration

C. @EnableAutoConfiguration 内部有一个@Import, 这个注解才是完成自动配置的关

键.

D. @Import 导入一个类(AutoConfigurationImportSelector),这个类内部提供了一个方

法(selectImports). 这个方法会扫描导入的所有 jar 包下的 spring.factories 文件. 解析文

件中自动配置类 key=value, 将列表中的类创建,并放到 Spring 容器中.

```

##### 11、SpringBoot项目的启动加载流程大概说下

- 分为两类:初始化SpringApplication,

- 运行SpringApplication,

其中运行SpringApplication的过程又可以细分为以下几个部分:

- SpringApplicationRunListeners 引用启动监控模块

- ConfigrableEnvironment配置环境模块和监听:包括创建配 境、加载属性配置文件和配置监听

- ConfigrableApplicationContext配置应用上下文:包括配置应用上下文对象、配置基本属性和刷新应用上下文

##### 12、SpringBoot项目读取配置文件的方式有几种?

- 需要注入 Environment 类, 使用 environment.getProperty(peorperties 中的 key), 这 样就能获得 key 对应的 value 值

- @Values(${key.value}) 直接读取

- 新建实体类用来映射该配置,实体类上加注解@ConfigurationProperties(prefix = 用来指定配置文件前缀)

##### 13、如何自定义SpringBoot starter?

- springboot加载starter原理,其实就是加载依赖jar包下的spring.factories文件。所以我们要自定义starter,就需要在项目中建立一个META-INF的文件夹,然后在文件夹下面建一个spring.factories文件,文件里将你需要提供出去的bean实例信息配置好就行。

##### 14、BeanFactory和FactoryBean的区别?

- 区别

- beanfactory其实就是bean容器的顶层接口,也就是用来管理bean对象的。很多容器都实现了它。

- factorybean是一个接口,如果一个类实现了这个接口,那么如果直接使用getBean(beanName)方式,得到的结果并不是这个实现类对象,而是实现类里面getObject()返回的对象,如果想要结果是这个实现类,则需使用getBean(&beanName)

##### 15、Spring Bean的生命周期是什么?

- 生命周期:入口方法

- ①实例化一个 Bean,也就是我们通常说的 new

- ②按照 Spring 上下文对实例化的 Bean 进行配置,也就是 IOC 注入 47

- ③如果这个 Bean 实现 dao 了 BeanNameAware 接口,会调用它实现的 setBeanName(String beanId)方法,此处传递的是 Spring 配置文件中 Bean 的 ID

④ 如果这个 Bean 实现了 BeanFactoryAware 接口,会调用它实现的 setBeanFactory(), 传递的是 Spring 工厂本身(可以用这个方法获取到其他 Bean)

- ⑤如果这个 Bean 实现了 ApplicationContextAware 接口,会调用 setApplicationContext(ApplicationContext)方法,传入 Spring 上下文,该方式同样可 以实现步骤 4,但比 4 更好,以为 ApplicationContext 是 BeanFactory 的子接口,有更多 的实现方法

- ⑥如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用 postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor 经常被 用作是 Bean 内容的更改,并且由于这个是在 Bean 初始化结束时调用 After 方法,也可用 于内存或缓存技术

- ⑦如果这个 Bean在 Spring 配置文件中配置了 init-method 属性会自动调用其配置的初始 化方法

- ⑧如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用 postAfterInitialization(Object obj, String s)方法 注意:以上工作完成以后就可以用这个 Bean 了,那这个 Bean 是一个 single 的,所以 一般情况下我们调用同一个 ID 的 Bean 会是在内容地址相同的实例

- ⑨当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 接口,会 调用其实现的 destroy 方法

- ⑩ 最后,如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其 配置的销毁方法

##### 16.spring框架中单例bean是线程安全嘛

不安全

Spring中的Bean默认是[单例模式](https://so.csdn.net/so/search?q=%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F&spm=1001.2101.3001.7020)的,框架并没有对bean进行多线程的封装处理。

如果Bean是有状态的,那就需要开发人员自己来进行[线程安全](https://so.csdn.net/so/search?q=%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8&spm=1001.2101.3001.7020)的保证,最简单的办法就是改变bean的作

用域 把 "[singleton](https://so.csdn.net/so/search?q=singleton&spm=1001.2101.3001.7020)"改为’‘protopyte’ 这样每次请求Bean就相当于是 new Bean() 这样就可以保证线程的

安全了。

##### 17.spring框架中使用了那些设计模式及引用场景

1、工厂模式

在各种BeanFactory以及ApplicationContext创建中都有用到

2、模板模式

在各种BeanFactory以及ApplicationContext创建中都有用到

3、代理模式

SpringAOP利用了AspectJ AOP实现的,Aspectj AOP的底层用的就是动态代理

4、策略模式

a、用于加载资源文件,会使用不同的方法,比如:ClassPathResource,FileSystemResource,ServletContextResource,UrlResource,但是他们都实现了一个共同的接口Resource;

b、在Aop的实现中可以采用两种不同的方式,JDK的动态代理和cglib代理。

5、单例模式

比如我们在创建bean的时候

6、观察者模式

spring中的ApplicationListener,ApplicationEvent,ApplicationEventPublisher

7、适配器模式

MethodBeforeAdviceAdapter,ThrowAdviceAdapter,AfterReturningAdapter

8、装饰者模式

我们打开一些源码,可以看到一些类型带有Wrapper或者Decorator的都是的

##### 18.spring事务的实现方式原理是什么

1. spring事务的实现方式

spring框架提供了两种事务实现方式:编程式事务、声明式事务

编程式事务:在代码中进行事务控制。优点:精度高。缺点:代码耦合度高

声明式事务:通过@Transactional注解实现事务控制

2. spring事务的底层原理

事务的操作本来应该由数据库进行控制,但是为了方便用户进行业务逻辑的控制,spring对事务功能进行了扩展实现。一般我们很少使用编程式事务,更多的是使用@Transactional注解实现。当使用了@Transactional注解后事务的自动功能就会关闭,由spring帮助实现事务的控制。

Spring的事务管理是通过AOP代理实现的,对被代理对象的每个方法进行拦截,在方法执行前启动事务,在方法执行完成后根据是否有异常及异常的类型进行提交或回滚。

Spring AOP动态代理机制:

Spring在运行期间会为目标对象生成一个代理对象,并在代理对象中实现对目标对象的增强。

SpringAOP通过两种动态代理机制,实现对目标对象执行横向植入的。

代理技术 描述

JDK 动态代理 Spring AOP 默认的动态代理方式,若目标对象实现了若干接口,Spring 使用 JDK 的 java.lang.reflect.Proxy 类进行代理。

CGLIB 动态代理 若目标对象没有实现任何接口,Spring 则使用 CGLIB 库生成目标对象的子类,以实现对目标对象的代理。

##### 19,spring是如何简化开发的

Spring通过以下四种策略来简化java开发。

- 基于POJO的轻量级和最小侵入编程;

- 通过依赖注入和面向接口实现松耦合;

- 基于切面(AOP)和惯例进行声明式编程;

- 通过切面和模板减少样式代码,RedisTemplate,xxxTemplate。

##### 20.spring中的starter如何理解

Starter是Spring Boot的四大核心功能特性之一,除此之外,Spring Boot还有自动装配、Actuator监控等特性。

Spring Boot里面的这些特性,都是为了让开发者在开发基于Spring生态下的企业级应用时,只需要关心业务逻辑,

减少对配置和外部环境的依赖。

其中,Starter是启动依赖,它的主要作用有几个。

1. Starter组件以功能为纬度,来维护对应的jar包的版本依赖,

2. 使得开发者可以不需要去关心这些版本冲突这种容易出错的细节。

3. Starter组件会把对应功能的所有jar包依赖全部导入进来,避免了开发者自己去引入依赖带来的麻烦。

4. Starter内部集成了自动装配的机制,也就说在程序中依赖对应的starter组件以后,

5. 这个组件自动会集成到Spring生态下,并且对于相关Bean的管理,也是基于自动装配机制来完成。

6. 依赖Starter组件后,这个组件对应的功能所需要维护的外部化配置,会自动集成到Spring Boot里面,

7. 我们只需要在application.properties文件里面进行维护就行了,比如Redis这个starter,只需要在application.properties

8. 文件里面添加redis的连接信息就可以直接使用了。

在我看来,Starter组件几乎完美的体现了Spring Boot里面约定优于配置的理念。

##### **21.restcontroller和controller区别是什么?**

restcontroller和controller区别主要有以下:1.用Controller配合视图解析器才能返回到指定页面。在对应的方法上加上ResponseBody注解才能返回JSON,XML或自定义mediaType的内容到页面。2.不可以只用RestController注解Controller,因为这样会让Controller中的内容不能返回jsp页面,而且会直接返回Return里的内容。3.RestController相当于Controller和ResponseBood两者合并起来的作用。

## 四、Mysql & Mybatis?

#### 1、什么是索引?

​本质是帮助MySQL高效获取数据的数据结构

#### 2、Mysql的数据结构是什么(mysql索引的数据结构)?为什么用这种结构?(如何提高磁盘IO效率)

​Mysql的数据结构:

​MySql中主要应用的索引数据结构为B+Tree

​为什么用这种结构:

​由于数据存储于物理磁盘,所以要尽量减少从磁盘IO数据的次数

​IO次数取决于B+Tree的高度

​B+Tree把真实的数据放到叶子节点,数据项占的空间越小,数据项的数量越多,树的高度越低

#### 3、Mysql的数据IO查找流程是什么样的?

​1、对于聚集索引(主键索引)来说,也就是通过主键查询,一次查询就能查询到具体的行数据信息;

​2、对于非聚集索引来说(唯一索引、普通索引、全文索引),

​如果需要查询的数据列在索引中,如A+B联合索引,根据A去查询B,则通过一次查询就能直接拿到索引上的数据,也就是覆盖索引现象;

​如果需要查询的数据不在索引中,则需要先去普通索引树中进行第一次查找得到行数据的主键值,

​然后通过主键值去主键索引树中第二次搜索得到真实数据,这种需要二次查询的现象叫做回表查询

#### 4、B+tree和Btree由什么组成?他们的异同?

​异同:

​1、B+tree是Btree的变体

​2、在Btree的基础上增加了叶子节点的顺序访问指针,B+Tree提高了顺序访问的性能

​3、Btree每个节点的指针上限为2d+1,B+Tree每个节点的指针上限为2d

​4、B+Tree非叶子节点只存储索引值,叶子节点存储真实数据,Btree所有节点上都存储数据

#### 5、Mysql两种存储引擎(InnoDB和Mysiam)的区别?这两种引擎B+tree的叶子结点和非叶子结点分别存储的什么?

​区别:

​1、InnoDB支持事务和外键,MySIAM不支持

​2、InnoDB支持行锁,MySIAM只支持表锁

​3、InnoDB的真实数据和索引都存储在同一个文件中,MMySIAM存储在两个文件中

​4、InnoDB不支持全文索引,MySIAM支持

​InnoDB:

​叶节点data域保存了完整的数据记录

​MySIAM:

​叶节点的data域存放的是数据记录的地址

#### 6、Mysql索引有哪些类型?什么场景使用哪种索引?

​普通索引、唯一索引、主键索引、联合索引、全文索引

#### 7、如何进行Mysql优化?(sql优化层面和服务器优化层面)

​SQL层面:

​1、尽量避免使用select *

​2、规范sql语句大小写,sql时有缓存的,避免每次都需要解析

​3、分情况使用exsits代替in,(IN适合于外表大而内表小的情况,而EXISTS适合于外表小而内表大的情况。)

​4、mysql sql解析执行过程是从右到左,from后面能过滤掉更多数据的基础表放在后面,where后面能过滤掉更多数据的查询条件放在后面

​5、查询条件中用相同类型去查询,比如避免数值列用字符串查询条件

​6、合理使用索引

​7、explain命令进行sql慢查询排查

​服务器优化层面:

​1、读写分离:主节点写,从节点读

​2、分库:根据业务或者其他维度把数据存放到不同数据库

​3、分表:

​1、水平分表:字段都一样,分多张表存放不同时间范围或不同维度的数据,如实时数据表、历史数据表

​2、垂直分表:将不同字段放在多张表,使用外键关联

​4、常用分库分表中间件:阿里的Cobar及开源社区基于Cobar维护的Mycat

#### 8、Sql调优你会从何入手(措施)?

​explain命令进行sql慢查询排查:

​1、id:select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序,id相同,执行顺序从上至下,id值越大,优先级越高,越先执行

​2、select_type:查询类型 SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION、UNION RESULT

​SIMPLE:表示简单杳询,其中不包括连接查询和子查询;

​PRIMARY:表示主查询,或者最外层的查询语句;

​SUBQUERY:子查询中的第一个SELECT语句;

​DERIVED:导出表的SELECT (FROM语句的子查询);

​UNION:表示连接查询的第2个或后面的查询语句;

​UNION RESULT:连接查询的结果;

​3、table:正在访问哪个表

​4、partitions:匹配的分区

​5、type:访问的类型,效率从快到慢:

​NULL>system>const>eq_ref>ref>ref_or_null>index_merge>range>index>ALL

​6、possible_keys:显示可能应用在这张表中的索引,一个或多个,但不一定实际使用到

​7、key:实际使用到的索引,如果为NULL,则没有使用索引

​8、key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度

​9、ref:显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或常量被用于查找索引列上的值

​10、rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需读取的行数,这个数量越小越好

​11、filtered:查询的表行占表的百分比

​12、Extra:包含不适合在其它列中显示但十分重要的额外信息

#### 9、Mysql中如何合理使用索引?有哪些会使索引失效的情况?

​1、为合适的列添加索引(主键、唯一索引、组合索引)

​2、尽量建立联合索引,也省空间成本

​3、尽量使用覆盖索引

​4、避免会使索引失效的操作

​会使索引失效的情况:

​1、索引列有null值不走索引

​2、使用is null或is not null不走索引

​3、各种负向查询not ,not in, not like ,<> ,!= ,!> ,!< 不会使用索引

​4、like将%放左边不走索引

​5、查询条件的数据类型做了隐式转换

​6、使用in或union代替or,or两侧有非索引列就不会走索引

​7、尽量保持索引列干净,不在索引列上使用函数转换、运算

​8、联合索引要遵循最左匹配原则

​9、使用比较运算或between会使联合索引从使用比较运算的下一个索引处断开

#### 10、Mysql如何排查慢查询(哪个关键字)?分别会列出来哪些信息项?

​explain命令进行sql慢查询排查:

​1、id:select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序,id相同,执行顺序从上至下,id值越大,优先级越高,越先执行

​2、select_type:查询类型 SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION、UNION RESULT

​SIMPLE:表示简单杳询,其中不包括连接查询和子查询;

​PRIMARY:表示主查询,或者最外层的查询语句;

​SUBQUERY:子查询中的第一个SELECT语句;

​DERIVED:导出表的SELECT (FROM语句的子查询);

​UNION:表示连接查询的第2个或后面的查询语句;

​UNION RESULT:连接查询的结果;

​3、table:正在访问哪个表

​4、partitions:匹配的分区

​5、type:访问的类型,效率从快到慢:

​NULL>system>const>eq_ref>ref>ref_or_null>index_merge>range>index>ALL

​6、possible_keys:显示可能应用在这张表中的索引,一个或多个,但不一定实际使用到

​7、key:实际使用到的索引,如果为NULL,则没有使用索引

​8、key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度

​9、ref:显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或常量被用于查找索引列上的值

​10、rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需读取的行数,这个数量越小越好

​11、filtered:查询的表行占表的百分比

​12、Extra:包含不适合在其它列中显示但十分重要的额外信息

#### 11、事务的特性是什么?Mysql事务隔离级别有哪几种?分别会产生什么问题?Mysql默认隔离级别是什么?Oracle呢?

​事务的特性:

​原子性、一致性、隔离性、永久性

​Mysql事务隔离级别:

​读未提交、不可重复读、可重复读、串行化

​分别会产生什么问题:

​读未提交:一个事物读到了另一个事务尚未提交的数据,不符合事务的隔离性

​不可重复读:同一个事务中针对同一行记录两次读出来的结果不一样,原因就是第二次读到了其他事务修改提交的数据

​可重复读:同一个事务中针对同一范围内的数据两次读出来的结果不一样,原因就是第二次读到了其他事务新增提交的数据

​mysql默认隔离级别:

​可重复读,但是一般会设置为不可重复读,因为在实际业务中常常是,一个事务中需要读到别的事务提交修改的数据

​oraclel默认隔离级别:

​不可重复读

#### 12、Mysql的行锁、表锁,悲观锁、乐观锁?

​行锁:

​访问数据库的时候,锁定整个行数据,防止并发错误。

​表锁:

​访问数据库的时候,锁定整个表数据,防止并发错误。

​乐观锁:

​乐观的认为本次事务操作数据不会有别的事务干扰,操作数据前不进行加锁,只是预先记录版本号,真正修改数据时再进行比对,

​如果版本号没变则修改数据,版本号变了则表名别的事务在本次事务过程中修改了数据,本次事务不修改数据。

​悲观锁:

​悲观的认为本次事务一定会有别的事务干扰,操作数据前必须先加锁(互斥锁,自旋锁,读写锁)

#### 13、Mysql的vachar和char的区别?

​vachar:

​长度为可变的,实际使用多少空间就占多少空间。

​char:

​列长度固定,为创建表时声明的长度,长度值范围是1到255,当char值未填满指定长度时,其他空间会用空格进行填充,检索CHAR值时需删除尾随空格。

#### 14、什么是内连接(inner join)、外连接(left join)?

​内连接:

​只显示左右两表中匹配条件的行,在结果表中删除与其他被连接表中没有匹配行的所有行

​左外连接:

​把左边表的数据全部取出来,而右边表的数据有相等的,显示出来,如果没有,显示NULL

#### 16、Mybatis底层的原理?一级缓存和二级缓存是什么?

​Mybatis底层的原理:

​动态代理

​一级缓存:

​Mybatis中sqlSession对象的缓存,当执行查询以后,查询的结果会同时存入到Sql Session提供的一块区域中,该区域的结构是一个Map,

​当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,的话直接拿出来用,

​当SqlSession对象消失时,mybatis的一级缓存也就消失了,

​同时一级缓存是SqlSession范围的缓存,当调用SqlSession的修改、添加、删除、commit(),close等方法时,就会清空一级缓存。

​二级缓存:

​Mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存,但是其中缓存的是数据而不是对象

#### 17、mybatis #{}和${}的区别?

​#{}方式能够很大程度防止sql注入

​${}的方式无法防止Sql注入

#### 18、Mysql存储过程、存储函数、触发器分别用来干嘛的?创建语法是什么?

​存储过程:

​存储过程把经常使用的SQL语句或业务逻辑封装起来,预编译保存在数据库中,当需要时从数据库中直接调用,省去了编译的过程。

​提高了运行速度同时降低网络数据传输量

​存储函数:

​参数可以有多个,也可以没有参数,必须有且只有一个返回值

​触发器:

​触发器的执行不是由程序调用,也不是由手工启动,而是由事件来触发、激活从而实现执行

#### 19、union和unionAll有什么区别?

​Union:

​对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序

​在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果

​Union All:

​对两个结果集进行并集操作,包括重复行,不进行排序

​如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了

#### 20、创建表、删除表、更新表字段语句?、

​创建表:

​create table 表名

​删除表:

​drop table 表名

​更新表字段:

​修改字段名称:ALTER TABLE 表名 CHANGE 旧字段名 新字段名 数据类型;

​修改字段数据类型:ALTER TABLE 表名 MODIFY 字段名 新数据类型;

#### 21、mysql左外连接语句的写法?

​Select A.name,B.name from A Left Join B on A.id=B.id;

#### 22、听过InnoDB的Mvcc技术吗?说下是什么?

​Mvcc:

​多版本并发控制技术,它使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,

​取而代之的是把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能

#### 23、Java实现动态代理有哪些方式?区别是什么?

​Java实现动态代理有哪些方式:

​jdk动态代理、cglib动态代理

​区别:

​jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。

​总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效。

​还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。

吧​如果没有上述前提,jdk动态代理不能应用。

​由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

## 五、多线程

#### 1、创建线程的方式有哪些?相比继承Thread类,实现Runable接口的好处是什么?

​创建线程的方式:

1.继承 Thread 类并重写 run 方法创建线程,实现简单但不可以继承其他类

2.实现 Runnable 接口并重写 run 方法。避免了单继承局限性,编程更加灵活,实

现解耦。

3..实现 Callable 接口并重写 call 方法,创建线程。可以获取线程执行结果的返回

值,并且可以抛出异常。

4.使用线程池创建(使用 java.util.concurrent.Executor 接口)

​好处:

​1、适合多个相同程序代码的线程去处理同一个资源

​2、可以避免由于Java的单继承性带来的局限性

​3、增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的

#### 2、线程的状态有哪些?

​1、新建状态

​2、就绪状态

​3、运行状态

​4、阻塞状态

​5、死亡状态

#### 3、run()和start()方法有哪些区别?

​run():

​是来完成实际的业务逻辑,当run()方法结束后,此线程就会终止

​start():

​是用来开启一个线程的,使线程处于就绪状态,即可以被JVM来调度执行

#### 4、实现线程间通讯的方法有哪些?

​1、wait():

​让线程处于冻结状态,被wait的线程会被存储到线程池中

​2、notify():

​唤醒线程池中的一个线程

​3、notifyAll():

​唤醒线程池中的所有线程

#### 5、wait、notify、notifyAll分别的作用是什么?可以用在同步代码块之外吗?为什么?

​分别的作用:

​wait():

​让线程处于冻结状态,被wait的线程会被存储到线程池中

​notify():

​唤醒线程池中的一个线程

​notifyAll():

​唤醒线程池中的所有线程

​可以用在同步代码块之外吗:

​不行

​为什么:

​因为这些方法是用于操作线程状态的方法,必须要明确到底操作的是哪个锁上的线程

#### 6、Sleep和Wait的区别?

​sleep():

​1、属于Thread类,表示让一个线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,不会马上进入运行状态

​2、sleep方法没有释放锁

​3、sleep必须捕获异常

​4、sleep可以在任何地方使用

​wait:

​1、属于Object,一旦一个对象调用了wait方法,必须采用notify()和notifyAll()方法唤醒该进程

​2、wait方法释放了锁

​3、wait不需要捕获异常

​4、wait、notify、notifyAll只能在同步控制方法或者同步控制块中使用

#### 7、什么是线程安全问题?什么情况下会产生?如何解决?

​线程安全问题:

​多个线程同时访问共享数据时可能会出现问题,称为线程安全问题

​什么情况下会产生:

​当多线程访问共享数据时,由于CPU的切换,导致一个线程只执行了关键代码的一部分,还没执行完此时另一个线程参与进来,导致共享数据发生异常

​如何解决:

​通过线程同步机制synchronized + 锁来解决线程安全问题

#### 8、什么是死锁?如何防止产生死锁?

​什么是死锁:

​死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去

​如何防止产生死锁:

1、避免一个线程同时获取多个锁;

2、避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。

3、尝试使用定时锁,使用Lock.tryLock(timeout)来替代使用内部[锁机制](https://so.csdn.net/so/search?q=锁机制&spm=1001.2101.3001.7020)。

4、对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

#### 9、Synchronized关键字的底层原理是什么?

​当一个线程第一次运行到 synchronized 代码,获取到了 myObject 对象的 monitor 的锁,然后计数器就会加1,

​然后第二次运行到 synchronized 代码,会再次获取 myObject 对象的 monitor 的锁,这个就是重入加锁了,然后计数器会再次加1,变成2,

​这个时候,其他的线程运行到第一次 synchronized 代码,会发现 myObject 对象的 monitor 锁的计数器是大于0的,

​就意味着被别人给加锁了,然后此时线程就会进入 block 阻塞状态,什么都干不了,就是等待获取锁

​如果第一个线程出了 synchronized 修饰范围的话,就会有一个 moninorexit 的指令,此时,在底层获取锁的线程就会对那个对象

​的 monitor 的计数器减1,如果有多次重入加锁,就会对应多次减1,直到最后,计数器是0

​然后,后面 block 阻塞的线程,会再次尝试获取锁,但是只有一个线程可以获取锁

#### 10、Synchronized可以用在哪些地方?分别的锁对象是什么?

​1、同步代码块:锁对象为括号中的对象

​2、同步方法:锁对象为当前对象this

​3、静态同步方法:锁对象为class字节码文件对象

#### 11、Synchronized和JUC下Lock锁的异同?

1、synchronized是java关键字,他是jvm层面; lock是接口(相应的实现比如ReentrantLock);

2、synchronized无法获取到锁的状态, lock可以获取到锁的状态;

3、synchronized是自动锁,如果代码执行完毕或发生异常, 他会自动释放锁的资源, lock手动锁,需要人工进行调用unlock方法进行释放锁的资源, 一般都是放在finally; 如果不释放会发生死锁;

4、synchronized如果线程a获取到锁的资源,发生阻塞,线程b会一直等待, 而lock如果获取不到锁的资源,线程不用等待就结束了;

5、synchronized是可重入、不可中断, 非公平, lock 可重入、 可判断、 可公平(两则皆可);

6、synchronized唤醒线程和等待线程则使用的是Object提供的notify()、wait();方法, 而lock是condition里面提供的signal()、await()方法、 而lock可以获取多个condition可以实现线程循序执行(比如:下面代码1.0);

013、Synchronized在JDK1.6做了什么优化?

​自适应的CAS自旋、锁消除、锁粗化、偏向锁、轻量级锁

#### 12、Synchronized是公平锁还是非公平锁?获取不到锁时会阻塞吗?

​非公平,会阻塞

#### 13、同步代码块中执行完wait/notify/notifyAll后会立马释放锁吗?

​不会,必须等其所在的同步代码块执行完才会释放锁

#### 14、Lock锁有哪些实现?分别的特点是什么?

​1、ReentrantLock

​2、ReentrantReadWriteLock类中的静态内部类ReadLock(读-写锁)

​3、ReentrantReadWriteLock类中的静态内部类WriteLock(读-写锁)

#### 15、JUC下Lock的监视器对象是哪个类?与Synchronized的监视器有什么异同?

​JUC下Lock的监视器对象:

​Condition

​异同:

​Lock可以很灵活的根据线程角色类型去创建Condition监视器对象,调用await()、signal()、signalAll()进行线程通讯调度,

​ 而synchronized使用Object对象本身作为监视器对象去调用wait() 、notify()、notifyAll()进行线程通讯调度。

#### 16、什么是线程可重入?Synchronized具备吗?Lock呢?

​重入锁就是一个线程能否获取一个已经由它自己持有的锁。如果可以,就是可重入锁,否则为不可重入锁。它的作用就是能够避免重复获取的时候出现死锁

​synchronized 是可重入锁

​Lock 是可重入锁

#### 17、什么是AQS?

​是一个用于构建锁和同步容器的队列同步器,它是整个JUC包下Lock体系的核心,

​如ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore都是基于它来实现的,

​它解决了在实现同步容器时设计的大量细节问题,

​它的核心构成部分为:使用一个 先进先出的FIFO的队列存储排队等待锁的线程,使用一个用volatile修饰的int类型的state同步状态来记录

​当前是否有线程持有锁,0表示没有线程获得锁,1表示有,上锁state就加1,释放锁就对应减1,有重入锁现象,这个值就大于1,然后需要逐级去释放。

#### 18、什么是CAS?什么是CAS的ABA问题?如何解决?

​CAS:

​CAS其实就是乐观锁的一种实现方式

​ABA问题:

​1、线程1读取出指定内存地址的数据A,加载到寄存器,此时读取出来的原值不仅将作为要被计算的值A,还会作为比较值A。

​2、此时线程1的cpu被线程2抢占了,线程2也从同样的内存地址中读取了同样的数据A,线程2还比线程1先执行完,线程2产生了新数据B,

​ 并且遵守了CAS原理把新数据B存入该内存地址。 (这个时候内存的值由A被改为B)

​3、还没完,线程2执行完之后,线程1又没抢过其它线程,此时cpu被线程3抢占,之后步骤和第 2 步一样,线程3从同样的内存地址中读取了数据B,

​ 线程3还比线程1先执行完,线程3产生了新数据A(没错,与一开始的A相等,但值相等并不意味着此A就是彼A,已经被替换了),

​ 并且遵守了CAS原理把新数据A存入该内存地址。(这个时候内存的值由B又变为A)

​4、这个时候线程1执行完了,要遵守CAS原理存入数据,然后比较值A是原来的A(简称原A),而执行内存地址中的A是被替换过的了,

​ 但原A的值与内存中的A值是相等的,根据CAS,线程1会把新的执行结果存入该内存地址

​ 在实际业务中,两个数的值相等,但这两个数并不是同一个数

​解决方案:

​加个版本号,多一步比较版本号即可解决

#### 19、你了解JUC下的哪些工具类,分别有什么作用?(CountdownLatch、Cyclicbarrier、Simephore)

​CountdownLatch:

​利用它可以实现类似计数器的功能

​Cyclicbarrier:

​字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行

​Simephore:

​信号量,Sem、aphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可

#### 20、说下volatile关键字,有什么作用?原理是什么?

​是一个变量类型修饰符,被voltile修饰的变量具有以下特性:

​可见性:

​保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

​实现(缓存共享协议):

​对于用volatile形容的变量,线程写入本地内存中的同时会将数据立即刷新到主内存中。

​他线程读取该变量时,发现被volatile修饰,会将本地变量值置为无效,然后从主内存中读取。

​有序性:

​禁止进行指令重排序。为提高执行效率,在不影响最终执行结果的前提下,代码在编译成字节码的时候有可能进行指令重新

​排序,这在单线程情况下是没有问题的,但是在多线程的情况下会出现问题。volatile修饰的变量则可以避免这个问题。

​不保证原子性:

​volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。关于volatile 原子性可以理解为把对volatile

​变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步。

#### 21、说下ThreadLocal,有什么作用?有哪些主要方法,实现原理是什么?为什么会有内存泄漏问题?如何解决?

​ThreadLocal:

​ThreadLocal是除了加锁这种同步方式之外的另一种可以规避出现多线程安全问题的思路。

​ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,

​在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题

​主要方法:

​get()、set()、remove()

​实现原理:

​每个线程都有属于自己的一个ThreadLocalMap,可通过Thread获得,这个map存储着以threadLock对象为key、以设置的值为value的键值对。

​调用get或者set还有remove方法都是操作这个map

​内存泄漏:

​对于线程池里面不会销毁的线程, 里面总会存在着<ThreadLocal, LocalVariable>的强引用,

​因为 final static 修饰的 ThreadLocal 并不会释放,

​而 ThreadLocalMap 对于 Key 虽然是弱引用, 但是强引用不会释放, 弱引用当然也会一直有值,

​同时创建的 LocalVariable 对象也不会释放, 就造成了内存泄露;

​解决方案:

​为了避免出现内存泄露的情况, ThreadLocal 提供了一个清除线程中对象的方法, 即 remove,

​其实内部实现就是调用 ThreadLocalMap 的 remove 方法:

#### 22、说下线程池的几大核心参数?分别有什么作用?有几种默认的线程池?他们的7个核心参数为什么要那么设置?

​线程池的七大核心参数:

​1、核心线程数:

​没达到核心线程数时,会创建新的线程。当达到核心线程数时,任务会进去队列

​2、最大核心线程数:

​当达到核心线程数时,会去创建额外的线程来执行任务,最多不超过最大线程数

​3、存活时间

​当任务处理完成,额外的线程存活一段时间后,会自行销毁

​4、存活时间单位:

​空闲等待时间的单位

​5、阻塞队列:

​利用什么队列来存放任务,有界队列、无界队列等

​6、线程池工厂:

​线程创建工厂

​7、拒绝策略:

​分为四大策略:

​1、AbortPolicy(默认策略):直接抛出异常,阻止系统正常运行

​2、CallerRunsPolicy 策略:等线程池处理不了的任务,谁提交的任务,就给谁拿回去,让其自己执行

​3、DiscardPolicy 策略:直接抛弃策略,异常也不会抛,什么都不做

​4、DiscardOldestPolicy 策略:丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务

​有几种默认的线程池:

​1、newCachedThreadPool:

​可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

​2、newFixedThreadPool:

​指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中

​3、newSingleThreadExecutor:

​单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。

​4、newScheduleThreadPool:

​定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行

#### 23、单例模式写法有哪几种?(懒汉和饿汉式)那么懒汉式中保证线程安全的写法是什么?为什么要用双重检查模式?

​单例模式写法:

​1、懒汉式

​直到使用前才会创建实例

​2、饿汉式

​类加载的时候就创建实例

​懒汉式中保证线程安全:

​在 getInstance() 方法添加 synchronized 关键字,可以解决线程安全问题

​ 为什么要用双重检查模式:

​第一次校验:

​由于单例模式只需要创建一次实例,如果后面再次调用getInstance方法时,则直接返回之前创建的实例,

​因此大部分时间不需要执行同步方法里面的代码,大大提高了性能。

​如果不加第一次校验的话,那跟上面的懒汉模式没什么区别,每次都要去竞争锁。

     第二次校验:

​如果没有第二次校验,假设线程t1执行了第一次校验后,判断为null,这时t2也获取了CPU执行权,也执行了第一次校验,判断也为null。

​接下来t2获得锁,创建实例。

​这时t1又获得CPU执行权,由于之前已经进行了第一次校验,结果为null(不会再次判断),获得锁后,直接创建实例。

​结果就会导致创建多个实例。所以需要在同步代码里面进行第二次校验,如果实例为空,则进行创建

## 六、Redis & 多级缓存

### Redis

##### 1、项目中redis如何使用(结合业务)

缓存手机号和验证码

缓存文章的点赞信息 用了hash结构,大K是文章的id 小k 是用户的id value 是个标识

分布式锁(场景)

....

##### 2、redis的数据类型

string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

##### 3、redis的持久化方式 两者的区别 实际开发使用

redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。

RDB :持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)

优点:RDB 是一个非常紧凑(compact)的文件,它保存了 Redis 在某个时间点上的数据集,适合用于备份,适用于灾难恢复,遇到问题也可以将数据还原到不同的版本

缺点: 因为RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据。每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ,但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。

AOF :持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。Redis 还可以同时使用 AOF 持久化和 RDB 持久化。 在这种情况下, 当 Redis 重启时, 它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。你甚至可以关闭持久化功能,让数据只在服务器运行时存在。

优点:使用 AOF 持久化会让 Redis 变得非常耐久,Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

缺点:对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。

一般来说,如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快, 除此之外, 使用 RDB 还可以避免之前提到的 AOF 程序的 bug 。因为以上提到的种种原因, 未来我们可能会将 AOF 和 RDB 整合成单个持久化模型。 (这是一个长期计划。)

##### 4、redis是单线程,为什么那么快

1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于`HashMap`,`HashMap`的优势就是查找和操作的时间复杂度都是O(1);

2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;

3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

4、使用多路I/O复用模型,非阻塞IO;

5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

##### 5、怎么理解IO多路复用

多路I/O复用模型是利用 `select、poll、epoll` 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(`epoll` 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。

这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。

##### 6、主从复制的原理

1、Mysql主从复制原理

依据mysql底层的二进制日志文件binlog

1). MySQL master 将数据变更写入二进制日志( binary log)

2). slave将master的binary log拷贝到它的中继日志(relay log)

3). slave重做中继日志中的事件,将数据变更反映它自己的数据

(1)master服务器在内存缓冲区中给每个slave服务器都维护了一份同步备份日志(in-memory backlog),缓存最近一段时间的数据,默认大小1m,如果超过这个大小就会清理掉。

(2)同时,master 和 slave 服务器都维护了一个复制偏移量(replication offset)和 master线程ID(master run id),每个slave服务器在跟master服务器进行同步时都会携带master run id 和 最后一次同步的复制偏移量offset,通过offset可以知道主从之间的数据不一致的情况。

(3)当连接断开时,slave服务器会重新连接上master服务器,然后请求继续复制。假如主从服务器的两个master run id相同,并且指定的偏移量offset在同步备份日志中还有效,复制就会从上次中断的点开始继续。如果其中一个条件不满足,就会进行完全重新同步,因为主运行id不保存在磁盘中,如果从服务器重启的话就只能进行完全同步了。

##### 7、什么情况下需要全量更新

主从第一次建立连接时,master的offset远远超过了slave的offset,master继续写入新数据,其offset就会覆盖旧的数据,直到将slave现在的offset也覆盖,只能做全量同步。

##### 9、增量更新的原理

主从第一次同步是全量同步,但如果slave重启后同步,则执行增量同步。复制备份日志(repl_baklog)大小有上限,写满后会覆盖最早的数据。如果slave断开时间过久,导致尚未备份的数据被覆盖,则无法基于log做增量同步,只能再次全量同步。

##### 10、哨兵机制的作用··

监控:监控master和slave,不断的检查master和slave是否正常运行,master存活检测、master与slave运行情况检测

通知(提醒):当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送通知

自动故障转移:断开master与slave连接,选取一个slave作为master,将其他slave连接新的master,并告知客户端新的服务器地址

注意:哨兵也是一台redis服务器,只是不提供数据相关服务,通常哨兵的数量配置为单数

##### 11、哨兵自动故障转移的算法(如何选择)

**(1)Raft简单介绍**

哨兵的选举采用的是Raft算法,Raft是一个用户管理日志一致性的协议,它将分布式一致性问题分解为多个子问题:**Leader选举**、**日志复制**、**安全性**、**日志压缩**等。Raft将系统中的角色分为领导者(Leader)、跟从者(Follower)和候选者(Candidate):

- Leader:接受客户端请求,并向Follower同步请求日式,当日志同步到大多数节点上后告诉Follower提交日志。

- Follower:接受并持久化Leader同步的日志,在Leader告知日志可以提交之后,提交日志。

- Candidate:Leader选举过程中的临时角色。

**(2) Term(任期)**

> 在分布式系统中,各个节点的时间同步是一个很大的难题,但是为了识别过期时间,时间信息有必不可少。Raft协议为了解决这个问题,引入了term(任期)的概念。

**Raft算法将时间划分为任意不同长度的任期(term)**。任期用连续的数字进行表示。每**一个任期的开始都是一次选举(election),一个或多个候选人会试图成为领导人**,如果一个候选人赢得了选举,它就会在该任期的剩余时间担任领导人。在某些情况下,选票会被瓜分,有可能没有选出领导人,那么将会开始另一个任期,并且立刻开始下一次选举。**Raft算法保证在给定的一个任期内最多是有一个领导人**。

**(3) RPC**

**Raft算法中服务器节点之间通信使用远程过程调用(RPC)**,并且基本的一致性算法只需要两种类型的RPC,为了在服务器之间传输快照增加了第三种 RPC。

- **RequestVote RPC**:候选人在选举期间发起。

- **AppendEntries RPC**:领导人发起的一种心跳机制,复制日志也在该命令中完成。

- **InstallSnapshot RPC**:领导者使用该RPC来发送快照给太落后的追随者。

**(4) 选举流程**

> **redis中的纪元(epoch)**:使用了类似于Raft算法term(任期)的概念称为epoch(纪元),用来给时间增加版本号。主要有两种:

>

> - **currentEpoch**:它的作用在于,当集群的状态发生改变,某个节点为了执行一些动作需要寻求其他节点的统一时,就会增加currentEpoch的值。目前curretnEpoch只用于slabe的故障转移流程。

> - **configEpoch**:这是一个集群节点配置相关的概念,每个集群节点都有自己独一无二的configepoch,所谓的节点配置,实际上是指节点所负责的槽位信息。每一个master在向其他节点发送包时,都会附带其configEpoch信息,以及一份表示它负责的slots信息。

- 1、某个Sentinel认定master客观下线的节点后,该Sentinel会先看看自己有没有投过票,如果自己已经投过票给其他Sentinel了,在2倍故障转移的超时时间自己就不会成为Leader。相当于它是一个Follower。

- 2、如果该Sentinel还没投过票,那么它就成为Candidate。

- 3、和Raft协议描述的一样,成为Candidate,Sentinel需要完成几件事情

> - 1)更新故障转移状态为start

> - 2)当前epoch加1,相当于进入一个新term,在Sentinel中epoch就是Raft协议中的term。

> - 3)更新自己的超时时间为当前时间随机加上一段时间,随机时间为1s内的随机毫秒数。

> - 4)向其他节点发送is-master-down-by-addr命令请求投票。命令会带上自己的epoch。

> - 5)给自己投一票,在Sentinel中,投票的方式是把自己master结构体里的leader和leader_epoch改成投给的Sentinel和它的epoch。

- 4、其他Sentinel会收到Candidate的is-master-down-by-addr命令。如果Sentinel当前epoch和Candidate传给他的epoch一样,说明他已经把自己master结构体里的leader和leader_epoch改成其他Candidate,相当于把票投给了其他Candidate。投过票给别的Sentinel后,在当前epoch内自己就只能成为Follower。

- 5、Candidate会不断的统计自己的票数,直到他发现认同他成为Leader的票数超过一半而且超过它配置的quorum(quorum可以参考《redis sentinel设计与实现》)。Sentinel比Raft协议增加了quorum,这样一个Sentinel能否当选Leader还取决于它配置的quorum。

- 6、如果在一个选举时间内,Candidate没有获得超过一半且超过它配置的quorum的票数,自己的这次选举就失败了。

- 7、如果在一个epoch内,没有一个Candidate获得更多的票数。那么等待超过2倍故障转移的超时时间后,Candidate增加epoch重新投票。

- 8、如果某个Candidate获得超过一半且超过它配置的quorum的票数,那么它就成为了Leader。

- 9、与Raft协议不同,Leader并不会把自己成为Leader的消息发给其他Sentinel。其他Sentinel等待Leader从slave选出master后,检测到新的master正常工作后,就会去掉客观下线的标识,从而不需要进入故障转移流程。

**大致简单过程**

> 1、每个做主观下线的sentinel节点像其他sentinel节点发送命令,要求将自己设置为领导者

> 2、接收到的sentinel可以同意或者拒绝

> 3、如果该sentinel节点发现自己的票数已经超过半数并且超过了quorum

> 4、如果此过程选举出了多个领导者,那么将等待一段时重新进行选举

##### 12、工作redis几台服务器

三主三从

##### 13、说一说散列插槽

在构建redis集群时,会生成16384(0-16383)个[插槽](https://so.csdn.net/so/search?q=插槽&spm=1001.2101.3001.7020)(slot),这些插槽会被分成几部分存储在每个master节点上,每个master之间存储的slot块是不重复的。

当我们在集群中进行存取数据时,首先根据key中是否包含{ },如果包含{ },会把{}内的内容进行某种hash运算得到一个数值,然后在和16384进行取余,就会得到这个key所在的slot块。如果key中不包含{ },就会把整个key的值进行某种hash运算得到一个数值,然后进行和16384进行取余,得到所对应的slot块。

##### 14、redis的删除策略

redis中的数据删除策略包括定时删除、惰性删除、定期删除

定时删除:创建一个定时器,当key设置有过期时间,且过期时间到达时,立即执行key的删除操作。优点:节约内存,到时就删除,立即释放不必要的内存占用。缺点:CPU压力较大,无论CPU此时负载量多高,均占用CPU,会影响redis服务器响应时间和指令吞吐量。总结:用处理器性能换取存储空间(时间换空间),适用于小内存,强CPU场景。

惰性删除:数据到达过期时间,先不做处理。等下次访问该数据时,发现数据已过期,删除,给客户端返回不存在。只要是调用操作数据的指令,都会先执行expireIfNeeded()。优点:节约CPU性能,发现不得不删除的时候才删除。缺点:内存空间压力很大,出现长期占用内存的数据。总结:用存储空间换取处理器性能 (空间换时间),适用于大内存,弱CPU场景

定期删除:每个库都有独自维护的过期库expires,每秒钟执行server.hz次serverCron,serverCron会轮询所 有的库,使用databasesCron方法对每个库进行检测,databasesCron会调用activeExpireCycle会对每个expire[]检测,一个expire[]检测250ms/server.hz。周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度。

特点1:CPU性能占用设置有峰值,检测频度可自定义设置。特点2:内存压力不是很大,长期占用内存的冷数据会被持续清理

总结:周期性抽查存储空间(查询某个库的expires时,如果这轮删除过多,则再抽取删除一轮,如果这轮删除的很少,则去检查下一个库的expires)

##### 15、redis的淘汰策略(说说怎么保证redis都是热点数据)

**noeviction**:不进行数据淘汰,也是Redis的默认配置。这时,当缓存被写满时,再有写请求进来,Redis不再提供服务,直接返回错误。

2.volatile-random:

缓存满了之后,在设置了过期时间的键值对中进行随机删除。

3.volatile-ttl

缓存满了之后,会针对设置了过期时间的键值对中,根据过期时间的先后顺序进行删除,越早过期的越先被删除。

4.volatile-lru

缓存满了之后,针对设置了过期时间的键值对,采用LRU算法进行淘汰。

5.volatile-lfu

缓存满了之后,针对设置了过期时间的键值对,采用LFU的算法进行淘汰。

6.allkeys-random

缓存满了之后,从所有键值对中随机选择并删除数据。

7.allkeys-lru

缓存满之后,使用LRU算法在所有的数据中进行筛选删除。

8.allkeys-lfu

缓存满了之后,使用LFU算法在所有的数据中进行筛选删除。

##### 16、缓存雪崩

大量的应用请求无法在Redis缓存中进行处理,紧接着应用将大量请求发送到数据库层,导致数据库层的压力激增。

原因一:缓存中有大量Key同时过期,导致大量请求无法得到处理,大量数据需要回源数据库。

解决方案

方案一 :差异化设置过期时间,不要让大量的 Key 在同一时间过期。比如,在初始化缓存的时候,给这些数据的过期时间增加一个较小的随机数,这样一来不同数据的过期时间有所差别又差别不大,即避免了大量数据同时过期又能保证这些数据在相近的时间失效

方案二:服务降级,允许核心业务访问数据库,非核心业务直接返回预定义的信息

方案三:不设置过期时间,初始化缓存数据的时候设置缓存永不过期,然后启动一个后台线程 30 秒一次定时把所有数据更新到缓存,而且通过适当的休眠,控制从数据库更新数据的频率,降低数据库压力。

原因二:Redis实例发生故障宕机,无法处理请求,就会导致大量请求积压到数据库层

解决方案

方案一 :服务熔断,暂停业务应用对缓存服务的访问,从而降低对数据库的压力

方案二:请求限流,控制每秒进入应用程序的请求数,避免过多的请求被发到数据库

方案三:Redis构建高可靠集群,通过主从节点的方式构建Redis高可靠集群。可以保证在Redis主节点故障宕机时,从节点切换到主节点,继续提供服务,避免由于缓存实例宕机导致缓存雪崩

##### 17、缓存穿透★★★★★

客户端请求了一个业务程序中不存在的数据,即要访问的数据既不在Redis缓存中,也不在数据库中。导致每次类似的请求,都会先从Redis–>数据库去查数据,最终也无果。当存在大量请求这样访问时,会同时给缓存和数据库带来很大压力。

出现原因:缓存中的数据和数据库中的数据都被误删了。恶意攻击:专门访问数据库中不存在的数据。Redis获取到null数据未进行持久化。

解决方案:

1.缓存null,对查询结果为null的数据进行缓存(长期使用,定期清理),设定短时限,例如30-60秒,最高5分钟

2.白名单策略,提前预热各种分类数据id对应的bitmaps,id作为bitmaps的offset,相当于设置了数据白名单。当加载正常数据时放行,加载异常数据时直接拦截(效率偏低)

使用布隆过滤器(有关布隆过滤器的命中问题对当前状况可以忽略)

3.实施监控:实时监控redis命中率(业务正常范围时,通常会有一个波动值)与null数据的占比

非活动时段波动:通常检测3-5倍,超过5倍纳入重点排查对象

活动时段波动:通常检测10-50倍,超过50倍纳入重点排查对象

根据倍数不同,启动不同的排查流程。然后使用黑名单进行防控(运营)

4.key加密:问题出现后,临时启动防灾业务key,对key进行业务层传输加密服务,设定校验程序,过来的key校

验。例如每天随机分配60个加密串,挑选2到3个,混淆到页面数据id中,发现访问key不满足规则,驳回数据访问

##### 布隆过滤器

##### 18、缓存击穿

热点数据在某一时刻过期了,可能会导致随之而来的大量的并发请求直接打到数据库,导致数据库压力激增。

解决方案:

分布式锁,采用锁机制控制打到数据库的请求,读取数据后将其写回到缓存,保证了其他线程可以命中缓存

热点数据不设置过期时间

##### 19、缓存预热

缓存预热就是系统上线后,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

解决方案:

1)直接写个缓存刷新页面,使用脚本程序固定触发数据预热过程。2)数据量不大,可以在项目启动的时候自动进行加载。3)定时刷新缓存。4)如果条件允许,使用了CDN(内容分发网络),效果会更好

##### 20、分布式锁(难点 可选)

#####可以使用redis实现分布式锁 (性能高)

​setnx key value

​此方式有问题,如果setnx获得锁之后,业务逻辑出了异常,这时候就会导致锁无法释放

​解决方案是设置锁的过期时间 使用set设置 (set key value ex 3000 nx)

​此方案也有问题,假设有两个线程分别是A,B。若A首先获得锁,但业务逻辑有异常,然后锁的定时时间到了自动释放了锁,此时线程B拿到了锁,但过了一段时间后,线程A处理完了异常,释放了锁,此时释放的锁就是B的锁了,造成了误删。用uuid保证误删。

此方案也有问题,删除锁的操作缺乏原子性

解决方案是使用LUA脚本

##### 21、Redis是一个什么样的数据库?读写速度怎么样?

Redis(Remote Dictionary Server) 是一个使用 C 语言编写的,开源的(BSD许可)高性能非关系型(NoSQL)的键值对数据库。

读写速度快

完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1);

数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;

采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

使用多路 I/O 复用模型,非阻塞 IO;

使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

##### 22、Redis主从结构能解决什么问题?

- 单点机器故障导致数据丢失,准备多台服务器,互相连通。将数据复制多个副本保存在不同的服务器上,连接在一起,保证数据的式同步的。一台服务器宕机,其他的服务器依然可以提供服务,redis的高可用,实现数据备份。

- 缓解单机的请求压力:主从实现读写分离,master写操作,slave读操作

##### 23、Redis主从同步具体流程是什么?

- 1)、建立连接

​2)、数据同步(全量同步、增量同步)【runid运行ID、offset偏移量、复制积压缓冲区】

​1、全量同步具体场景有哪些?流程?如何避免全量同步次数(全量同步非常耗时)

- 场景:

​1.从库第一次向主库请求同步数据

​2.repl_baklog大小有限制,写满后会覆盖最早的数据。如果slave断开时间过久,导致尚未备份的数据被覆盖,则无法基于log做增量同步,只能全量同步

- 流程:

​1.slave节点请求增量同步

​2.master节点判断replid,发现不一致,拒绝增量同步

​3.master将完整内存数据生成RDB,发送RDB到slave

​4.slave清空本地数据,加载master的RDB

​5.master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave

​6.slave执行接收到的命令,保持与master之间的同步

- 避免全量同步:

​1.一旦master重启,runid将发生变化

​2.修改复制缓冲区大小

​2、增量同步具体场景有哪些?流程?

- 增量同步到的场景:

​slave节点断开又恢复,并且在repl baklog中能找到offset时

- 流程:

​3)、命令传播

- 当master数据库状态被修改后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一致的状态,同步的动作称为命令传播

- master将接收到的数据变更命令发送给slave,slave接收命令后执行命令

##### 24、如何优化主从同步效率?

​1)、从尽量避免全量同步的方面入手(安全重启使runid不发生变化、调大复制积压缓冲区)

​2)、避免slave从结点太多造成复制风暴(使用树状拓补结构)

##### 25、Redis的故障恢复依靠什么机制?哨兵机制的主要工作范围、工作流程和作用?

故障恢复:哨兵机制sentinel监视集群中所有的redis实例

- **监控**:Sentinel 会不断检查您的master和slave是否按预期工作:

- 基于心跳机制,每隔1秒向集群每个实例发送ping命令

- 单个sentinel节点发现某实例未在规定时间响应,主观下线

- 超过一半的sentinel都认为该实例主观下线,则该实例客观下线

- **自动故障恢复**:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主

- 选择的依据

- 首先会判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds * 10)则会排除该slave节点

- 然后判断slave节点的slave-prority值,越小优先级越高,如果是0则永不参与选举

- 如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高

- 最后是判断slave节点的运行id大小,越小优先级越高。

- 如何实现切换:

- sentinel给备选的slave1节点发送slaveof no one命令,让该节点成为master

- sentinel给所有其它slave发送slaveof 192.168.150.101 7002 命令,让这些slave成为新master的从节点,开始从新的master上同步数据。

- 最后,sentinel将故障节点标记为slave.

- **通知**:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端

##### 26、Redis的线程模型是什么样的?(典型的NIO,非阻塞式IO)

- NIO,非阻塞式IO,IO多路复用:单个线程同时处理多个IO请求

- 用户将想要监视的文件描述符(File Descriptor)添加到select/poll/epoll函数中,由内核监视,函数阻塞。一旦有文件描述符就绪(读就绪或写就绪),或者超时(设置timeout),函数就会返回,然后该进程可以进行相应的读/写操作。

##### 27、Redis过期数据的删除策略是什么?有哪些?

- 数据过期删除策略:略就是针对已过期数据的处理策略

- 1.定时删除

- 创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作

- 优点:节约内存,到时就删除,快速释放掉不必要的内存占用

- 缺点:CPU压力很大,无论CPU此时负载量多高,均占用CPU,会影响redis服务器响应时间和指令 吞吐量

- 总结:用处理器性能换取存储空间(拿时间换空间)

- 2.惰性删除

- 数据到达过期时间,不做处理。等下次访问该数据时,我们需要判断 1. 如果未过期,返回数据 2. 发现已过期,删除,返回不存在

- 优点:节约CPU性能,发现必须删除的时候才删除

- 缺点:内存压力很大,出现长期占用内存的数据

- 总结:用存储空间换取处理器性能(拿空间换时间)

- 3.定期删除

- 定期删除就是周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比 的方式控制删除频度

- 特点1:CPU性能占用设置有峰值,检测频度可自定义设置

- 特点2:内存压力不是很大,长期占用内存的冷数据会被持续清理

- 总结:周期性抽查存储空间(随机抽查,重点抽查)

##### 28、Redis的数据淘汰策略是什么?有哪些?

- 当新数据进入redis时,如果内存不足怎么办?

- 在执行每一个命令前,会调用freeMemoryIfNeeded() 检测内存是否充足。如果内存不满足新 加入数据的最低存储要求,redis要临时删除一些数据为当前指令 清理存储空间。清理数据的策略称为逐出算法。

- 第一类:检测易失数据(可能会过期的数据集)

- policy volatile-lru:挑选最近最少使用的数据淘汰

- volatile-lfu:挑选最近使用次数最少的数据淘汰

- volatile-ttl:挑选将要过期的数据淘汰

- volatile-random:任意选择数据淘汰

- 第二类:检测全库数据

- allkeys-lru:挑选最近最少使用的数据淘汰

- allkeLyRs-lfu::挑选最近使用次数最少的数据淘汰

- allkeys-random:任意选择数据淘汰,相当于随机

- 第三类:放弃数据驱逐

- no-enviction(驱逐):禁止驱逐数据(redis4.0中默认策略),会引发OOM(Out Of Memory)

##### 29、Redis的慢查询如何排查?

- 查看**慢查询日志**指令: **slowlog get**

- 首先设置 Redis 的慢日志[阈值](https://so.csdn.net/so/search?q=阈值&spm=1001.2101.3001.7020),只有超过阈值的命令才会被记录,这里的单位是微妙,例如设置慢日志的阈值为 5 毫秒,同时设置只保留最近 1000 条慢日志记录

- 设置完成之后,所有执行的命令如果延迟大于 5 毫秒,都会被 Redis 记录下来,我们执行 SLOWLOG get 5 查询最近5条慢日志

- 通过查看慢日志记录,我们就可以知道在什么时间执行哪些命令比较耗时

- 使用**–bigkeys -i 0.1**指令:**查找每种数据类型中最大的bigkey**,需要扫描全库,**会阻塞主线程**,所以最好放到从库统计,或者用-i指令不连续执行统计操作。

##### 30、如何正确使用Redis的分布式事务锁?(Zookeeper也可以实现分布式锁)

- redis作为分布式锁的原理:

Redis 中有一个命令`SETNX key value`,`SETNX` 是 “SET if not exists” (如果不存在,则 SET)的缩写。那么这条指令只在 `key` 不存在的情况下,将键 `key` 的值设置为 `value`。若键 `key` 已经存在,则 `SETNX` 命令不做任何动作。

有了上面命令做支撑,同时我们了解到 Redis 是单线程模型(不要去计较它的网络读写和备份状态下的多线程)。那么我们就可以这么实现,当一个服务器成功的向 Redis 中设置了该命令,那么就认定为该服务器获得了当前的分布式锁,而其他服务器此时就只能一直等待该服务器释放了锁为止。

- 使用redis分布式锁可能产生的一些问题:

- 锁释放问题

如果服务器A获得了redis的锁,但是在执行过程中出了问题,其他服务器就一直无法获得锁,就会造成死锁。这时我们可以使用try...catch...finally,将释放锁的过程放到finally代码块中,但是万一A服务器在拿到锁执行代码的过程中服务器宕机了呢?这时候就算放在finally里面也没有用,这时我们就需要用新办法来解决这个问题了。

- Redis超时机制

Redis 中允许我们设置缓存的自动过期时间,我们可以将其引入我们上面的锁机制中,这样就算 finally 语句块中的释放语句没有被正确执行,Redis 中的缓存也能在设定时间内自动过期,不会形成死锁。但是,如果又会有一个问题,就是如果我们设置的过期时间内,代码未执行完。这个时候锁就会自动释放,而其他的进程就有可能来争抢这把锁,而此时原来获得锁的进程也在同时运行,这就有可能导致超卖现象或者其他并发安全问题。

那么如何解决这个问题呢?思路很简单,就是每隔一段时间去检查当前线程是否还在运行,如果还在运行,那么就继续更新锁的占有时长。

- 加锁&解决归一化

简单来说,就是一个线程执行了加锁操作后,后续的解锁操作只能由该线程来执行,即加锁操作和解锁只能由同一线程来进行。

- 高并发

如果我们系统中利用 Redis 来实现分布式锁,而 Redis 的读写并发量约合 5 万左右。假设现在一个秒杀业务需要支持的并发量超过百万级别,那么如果这 100万的并发全部打入 Redis 中去请求锁资源,Redis 将会直接挂掉。所以我们现在应该来考虑如何解决这个问题,即如何在高并发的环境下保证 Redis 实现的分布式锁的可用性,接下来我们就来考虑一下这个问题。

解决这个问题的关键思想就是:分而治之,将商品库存分开放。我们在 Redis 中存储商品的库存数量时,可以将商品的库存进行“分割”存储来提升 Redis 的读写并发量。

同时,我们可以将分隔的不同的库存数据分别存储到不同的 Redis 服务器中,进一步提升 Redis 的并发量

##### 31、Redis的双写一致性如何保证?

- 保证双写一致性的策略:

- 1.先更新缓存,再更新数据库

问题很明显如果更新缓存成功,更新数据库失败,就会造成缓存的脏数据

- 2.先更新数据库,再更新缓存

如果再高并发的情况下,可能会存在如下的情况,线程A更新了数据库,如果由于网络或者其他的原因,线程A还没来得及更新缓存,这时候有一个进程B更新了数据库,更新了缓存,这时候进程A才更新缓存,这时候就会导致线程B对缓存的更新丢失了,像事务丢失的情况

- 3.先删除缓存,再更新数据库

这种策略可能已经避免掉了,策略2中缓存丢失的情况,但是再高并发的情况下,也会有不一致的情况,比如线程A做写操作,首先删除缓存然后准备跟新数据库,这时候,线程B执行了写操作,没有命中缓存,然后查询数据库,这时候读取的是旧值,并把查询到的旧值保存到缓存中,接着线程A完成了数据库的更新,这时候数据库和缓存又出现了不一致的情况,解决方案:我们只要再线程A,完成数据库的更新之后,稍作延迟再删除一次缓存,也叫做延迟双删。这里的延迟时间一定要大于业务的一次读操作的时间。

- 4.先更新数据库,再删除缓存

再高并发的情况下,也会有不一致的情况,比如线程A做读取数据的操作,正准备写入缓存的时候,线程B更新了数据库,然后执行了删除缓存的操作,这时候线程A才把旧值写入到缓存中,虽然这种情况出现的概率比较低,因为写操作的时候要大于一次读操作的时间的。解决方案:延迟双删,延时双删还是又问题的,如果删除缓存失败怎么办,当然是再次删除,不断的循环删除。删除失败后我们可以将要删除的key放入到队列中,然后尝试重复删除,直到删除成功。

##### 32、项目搭建多级缓存的好处是什么?实现多级缓存的流程是什么?(加分项)

```sh

#1. 什么是多级缓存

是指在整个系统架构的不同系统层级进行数据缓存、以提高访问效率

浏览器访问静态资源时,优先读取浏览器本地缓存

访问非静态资源(ajax查询数据)时,访问服务端

请求到达Nginx后,优先读取Nginx本地缓存

如果Nginx本地缓存未命中,则去直接查询Redis(不经过Tomcat)

如果Redis查询未命中,则查询Tomcat

请求进入Tomcat后,优先查询JVM进程缓存

如果JVM进程缓存未命中,则查询数据库

多级缓存的关键有两个:

一个是在nginx中编写业务,实现nginx本地缓存、Redis、Tomcat的查询

另一个就是在Tomcat中实现JVM进程缓存

```

##### 33、Redis的hash槽一共有多少个?数据是如何进行入槽的?如果实现动态扩容?

​ 16384

​redis会根据key的有效部分计算插槽值,是利用CRC16算法得到一个hash值,然后对16384取余,余数作为插槽,寻找插槽所在实例

​动态扩容:

​1.添加一个节点到集群中:创建新的实例,添加新节点到集群( )

​2.将部分插槽分配到新节点

##### 34、过期策略

1.先区分两个概念,被动删除与主动删除

1)被动删除:key再被操作时,Redis主动检查key是否过期,过期则删除;

优劣:对CPU友好,只有CPU在被操作时删除,不浪费CPU时间;对内存不友好,如果同时大量key过期,这些key在被使用前不会被删除造成资源浪费;

2)主动删除:Redis会定期随机扫描一批设置了过期时间的key并进行删除处理;当已用内存超过最大内存maxmemory时也会触发主动清除策略;

Redis采用的是定期删除 + 懒惰删除策略。

## 七、Rabbitmq

##### 1、Rabbitmq消息模式有哪些?你们用的哪种?(5种)

简单模式:生产者生产消息后,将消息发往队列,消费者会实时的监听队列中的消息。如果有消息则会执行消息

工作模式:一个队列被多个消费者监听,但是只能有一个消费者获得消息。

发布订阅:生产者发送一条消息,经过交换机转发到多个不同的队列,多个不同的队列对应多个不同的消费者。(做到广播的效果的话,要保证一个消费者对应一个队列)

**路由模式**:当交换机类型为direct类型时,根据队列绑定的**路由key**转发到具体的队列中存放消息。

topic模式:当交换机类型为topic类型时,根据队列绑定的**路由key模糊转发**到具体的队列中存放。

##### 2、Rabbitmq如何保证mq消息可靠性?(3大方面)

1. 生产者确认机制:开启生产者确认机制(**confirm机制、return机制**),确保生产者的消息能到达队列

​生产者--交换机:confirm机制,发送消息的时候,给每个消息设置ConfirmCallback

​交换机-队列:return机制,给RabbitTemplate配置一个ReturnCallback

2. 开启持久化功能,确保消息未消费前在队列中不会丢失,(默认情况都是持久的)

3. 消费者确认机制:开启消费者确认机制为auto自动,由spring确认消息处理成功后完成,调用channel.basicAck()。手动签收,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息。

4. 失败重试机制:**开启消费者失败重试机制(默认本地多次重试失败是直接丢弃消息),设置MessageRecoverer:多次重试失败后将消息投递到异常交换机,交由人工处理**

+ 直接丢弃(默认)

+ 重新入队

+ **发送到指定交换机(RepublishMessageRecoverer:关联异常队列和交换机)**

##### 3、Rabbitmq如何实现延时消息?(2种)

利用**死信交换机**实现延迟队列模式:

给消息的目标队列指定死信交换机(**这里可以给目标队列设置ttl超时时间**)

将消费者监听的队列绑定到死信交换机

发送消息时**给消息设置超时时间为20秒**

给队列设置ttl属性,进入队列后超过ttl时间的消息变为死信,给消息设置ttl属性,队列接收到消息超过ttl时间后变为死信。最后消费者接收死信里的消息即可实现了延时的效果。**当队列、消息都设置了TTL时,任意一个到期就会成为死信。**

使用**DelayExchange插件**实现延迟队列模式:

插件的使用也非常简单:声明一个交换机,交换机的类型可以是任意类型,只需要**设定delayed(延迟)属性为true即可**,然后声明队列与其绑定即可。

发送消息时,消息中一定要携带x-delay属性,指定延迟的时间:setHeader(“x-delay”,10000)

##### 4、什么是死信队列?什么样的消息会进入死信队列?

**死信队列又称之为”延迟队列“或者”延时队列”**,也是RabbitMQ队列的一种,指的是进入队列的消息会被延迟消费的队列,这种队列跟普通的队列相比,最大的差异在于消息一旦进入普通队列将会立即被消费处理,而**进入死信队列则会过一定的时间在被消费处理**。

当一个队列中的消息满足下列情况之一时,可以成为死信从而进入死信队列:

+ 消费者使用basic.reject或 basic.nack声明消费失败,并且消息的requeue参数设置为false

+ 消息是一个过期消息,超时无人消费

+ 要投递的队列消息满了,无法投递

##### 5、Rabbitmq如何解决消息堆积问题?(3种思路)

+ 增加更多消费者,提高消费速度。也就是我们之前说的work queue工作模式

+ 扩大队列容积,提高堆积上限

+ 使用惰性队列,**惰性队列**会尽可能的将消息存入磁盘中,而在消费者消费到相应的消息时才会被加载到内存中,它的一个重要的设计目标是能够支持更长的队列,即支持更多的消息存储。

##### 6、如何保证消息的幂等性?(从业务层面进行判断)

**MQ出现非幂等性的情况**:

+ 生产者重复发送消息给MQ:生产者发送消息后在**等待mq返回确认收到**提示时发生宕机,然后生产者又恢复了,这时候生产者没有收到确认消息提示,于是发送同一条消息给mq,就导致了mq中消息重复问题!!

+ MQ重复发送消息给消费者:也是消费者宕机,mq没有收到确认提示

幂等:在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同

消息的幂等性:就是即使多次收到了消息,也不会重复消费。所以保证消息的幂等性就是保证消息不会重复消费,这在开发中是很重要的。比如客户点击付款,如果点击了多次,那也只能扣一次费。

**保证消息幂等性**:

**生产者不重复发送消息到MQ**: mq内部可以为每条消息**生成一个全局唯一、与业务无关的消息id**,当mq接收到消息时,会先根据该id判断消息是否重复发送,mq再决定是否接收该消息。

**消费者不重复消费**:消费者怎么保证不重复消费的关键在于消费者端做控制,因为MQ不能保证不重复发送消息,所以应该**在消费者端控制**:即使MQ重复发送了消息,消费者**拿到了消息之后,要判断是否已经消费过**,如果已经消费,直接丢弃。

生产者发送消息时,添加消息的唯一id,消费者消费完后使用redis保存消息唯一id,如果是重复过来的消息,消费者先去redis中判断该消息是否消费过,没有再消费。

使用数据库中的唯一索引

使用set集合

##### rabbiMQ如何保证消息的顺序性?

保证消息的顺序性,就是拆分多个 queue,每个 queue 对应一个 consumer(消费者),就是多一些 queue 而已,确实是麻烦点;或者就一个 (队列)queue 但是对应一个 (消费者)consumer,然后这个consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理。

## 八、微服务

#### 1)、什么是微服务?解决微服务各种问题都用了哪些组件?

​微服务:

​一种良好的分布式架构方案

​1、优点:拆分粒度更小、服务更独立、耦合度更低

​2、缺点:架构非常复杂,运维、监控、部署难度提高

​组件:

​1、Nacos:

​服务注册中心和配置中心

​2、Ribbon:

​服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台

​3、Feign:

​基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求

​4、Hystrix:

​发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题

​5、Gateway:

​如果前端、移动端要调用后端系统,统一从Zuul网关进入,由Zuul网关转发请求给对应的服务

#### 2)、什么是单体架构、什么是分布式架构、什么是微服务架构?

​单体架构:

​将业务的所有功能集中在一个项目中开发,打成一个包部署

​优点:

​架构简单

​部署成本低

​缺点:

​耦合度高(维护困难、升级困难)

​分布式架构:

​根据业务功能对系统做拆分,每个业务功能模块作为独立项目开发,称为一个服务

​优点:

​降低服务耦合

​有利于服务升级和拓展

​缺点:

​服务调用关系错综复杂

​微服务的架构:

​给分布式架构制定一个标准,进一步降低服务之间的耦合度,提供服务的独立性和灵活性。做到高内聚,低耦合。

​因此,可以认为微服务是一种经过良好架构设计的分布式架构方案

#### 3)、微服务有哪些特点?

​单一职责:

​微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责

​自治:

​团队独立、技术独立、数据独立,独立部署和交付

​面向服务:

​服务提供统一标准的接口,与语言和技术无关

​隔离性强:

​服务调用做好隔离、容错、降级,避免出现级联问题

#### 2、远程调用

​1)、什么是Feign,用来做什么的?Feign底层调用是怎么实现的?底层协议是什么?优势是什么?

​Feign:

​Feign是一个声明式WebService客户端。

​使用Feign能让编写Web Service客户端更加简单,它的使用方法就是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准

​作用:

​使编写 java http 客户端变得容易,封装了Http调用流程,更适合面向接口化的编程习惯

​底层调用实现流程;

​1、基于面向接口的动态代理方式生成实现类

​2、根据Contract协议规则,解析接口类的注解信息,解析成内部表现

​3、基于RequestBean,动态生成Request

​4、使用Encoder将Bean转换成Http报文正文(消息解析和转码逻辑)

​5、拦截器负责对请求和返回进行装饰处理

​6、日志记录

​7、基于重试器发送HTTP请求

​8、发送Http请求

​底层协议:

​Https协议

​优势:

​ 1.feign 采用的是基于接口的注解

​2.feign 整合了 ribbon,具有负载均衡的能力

​3.整合了 Hystrix,具有熔断的能力

#### 2)、服务间调用,其中一个服务宕机了,这个时候怎么做呢?

​1、超时处理

​2、舱壁模式(隔离)

​3、熔断,降级

#### 3)、Ribbon是什么?负载均衡策略有哪些?底层原理是什么?默认是哪种?

​Ribbon:

​主要功能是提供客户端的软件负载均衡算法

​负载均衡策略:

​1、随机策略:

​随机选择server

​2、轮询策略:

​按照顺序选择server(ribbon默认策略)

​3、重试策略:

​在一个配置时间段内,当选择server不成功,则一直尝试选择一个可用的server

​4、最低并发策略:

​逐个考察server,如果server断路器打开,则忽略,再选择其中并发链接最低的server

​5、可用过滤策略:

​过滤掉一直失败并被标记为 circuit tripped 的 server,过滤掉那些高并发链接的 server(active connections超过配置的阈值)

​6、响应时间加权重策略:

​根据server的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低

​7、区域权重策略:

​综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,

​剔除不可用的Zone中的所有server

#### 4)、Ribbon是如何实现轮询的?如果让你自己实现轮询,如何实现?

​原理:

​rest 接口的第几次请求数 % 服务器集群总数量 = 实际调用服务器的下标,每次服务重启,rest接口计数从1开始

#### 5)、Feign和Ribbon的关系是什么?

​Feign 是在 Ribbon的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端

#### 6)、你们项目中如何使用Feign的(Feign的最佳实践)

​将Feign的Client抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用

#### 7)、Feign远程调用时的日志级别有哪些?

​1、NONE,无记录(DEFAULT)

    2、BASIC,只记录请求方法和URL以及响应状态代码和执行时间

    3、HEADERS,记录基本信息以及请求和响应标头

    4、FULL,记录请求和响应的头文件,正文和元数据

#### 8)、如何优化Feign的调用性能?

​1、配置连接池

​2、设置合理的日志级别(basic级别就ok了,强烈不建议使用full)

​7)、Feign的默认超时时间是多久?重试次数是几次?

​默认超时时间:

​1秒

​重试次数:

​1次

#### 8)、Dubbo服务注册与发现的原理?(官方原理图)

​1、服务器容器负责启动、加载、运行服务提供者

​2、服务提供者在启动时,向注册中心注册自己提供的服务

​3、服务消费者在启动时,向注册中心订阅自己需要的服务

​4、注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者

​5、服务消费者从提供者地址列表中基于负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用

​6、服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心

#### 9)、Dubbo负载均衡策略有哪些?默认是哪种?

​1、Random LoadBalance(默认):

​随机,按权重设置随机概率

​2、RoundRobin LoadBalance:

​轮询,按公约后的权重设置轮询比例

​3、LeastActive LoadBalance:

​最少活跃调用数,相同活跃数的随机

​4、ConstantHash LoadBalance:

​一致性 Hash,相同参数的请求总是发到同意提供者

#### 10)、Dubbo支持哪些通信协议?默认是哪种?一般用哪种协议?有什么好处?

​Dubbo支持9种通信协议:

​1、dubbo 协议

​2、rmi 协议

​3、hessian 协议

​4、http 协议

​5、webservice 协议

​6、thrift 协议

​7、memcached 协议

​8、redis 协议

​9、rest

​默认是哪种:

​dubbo 协议 (默认)

​一般用哪种协议:

​dubbo 协议

​好处:

​底层是TCP,效率快

#### 11)、注册中心挂了影响服务调用吗?为什么?

​不会

​因为启动dubbo时,消费者会从注册中心拉取注册的生产者的地址接口等数据,缓存在本地。每次调用时,按照本地存储的地址进行调用

#### 12)、Dubbo启动检查如何设置?多版本支持如何设置?

​启动检查:

​可以通过配置信息 check="false" 关闭检查

​多版本支持:

​@DubboService(version = “2.0.0”)

​@DubboReference(version = "2.0.0")

#### 13)、Dubbo的默认超时时间是多久?重试次数是几次?

​默认超时时间:

​1秒

​默认重试次数:

​2次

#### 14)、Dubbo进行服务注册和发现的核心注解是哪个?

​@DubboService

​@DubboReference

#### 15)、Dubbo服务如何进行监控和管理?

​dubbo-admin 监控中心

#### 2、注册中心

​1)、Eureka

​1、eureka是属于什么体系的技术(Spring Cloud)

​Spring Cloud

​2、eureka技术体系有哪些角色?(服务端用作注册中心,客户端用作微服务)

​服务端用作注册中心,客户端用作微服务

​3、eureka的自我保护机制是什么?什么时候开启?为什么开启?开启了会发生什么?

​自我保护机制:

​某时刻某个微服务不能用了,eureka不会立刻清理,而是对该微服务进行保存

​开启时间:

​当EurekaServer节点在短时间内丢失过多客户端时候(可能发生了网络分区故障)

​为什么开启:

​默认情况下,如果EurekaServer在一定时间内没有收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90s)。

​但是当网络分区故障发生(延时、卡顿、拥挤)时候,微服务与EurekaServer之间无法正常通信,

​以上行为可能变得非常危险了,因为微服务本身其实是健康的。此时本不应该注销这个微服务的

​开启了会发生什么:

​一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据。也就是不会注销任何微服务

#### 4、eureka作为注册中心的原理是什么?心跳检测某个服务是否健康的原理详细说下?

​原理:

​1、服务提供者启动后将注册到注册中心,提供IP, 名字,什么服务等信息,

​ 2、服务消费者作为客户端注册到注册中心后,拉取注册中心的服务列表,在通过负载均衡调用对应的服务提供者。

​ 3、注册中心可以建立集群,生成多台eureka,注册中心为了监测各个服务的心跳,将在每 30S 向所注册的服务发起请求判断

​ 4、服务是否挂掉,如果挂掉90S后将会将服务从注册中心剔除。

​ 一个服务可以监测多台服务实例,从而可实现均衡负载。

​心跳检测:

​在应用启动后,节点们将会向Eureka Server发送心跳,默认周期为30秒,

​如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,

​Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。

#### 5、eureka集群是属于AP还是CP?(AP)

​AP

#### 2)、Nacos

##### 1、nacos是属于什么体系的技术(Spring Cloud Alibaba)

​Spring Cloud Alibaba

##### 2、nacos作为注册中心的原理是什么?

​服务注册时在服务端本地会通过轮询注册中心集群节点地址进行服务注册,

​在注册中心上,即Nacos Server上采用了Map保存实例信息,当然配置了持久化的服务会被保存到数据库中,

​在服务的调用方,为了保证本地服务实例列表的动态感知,Nacos与其他注册中心不同的是,采用了 Pull/Push同时运作的方式

##### 3、nacos如何确定唯一的一个服务?(通过namespace、group、service、集群唯一确定一个服务)

​通过namespace、group、service、集群唯一确定一个服务

##### 4、nacos中namespace、group分别的作用是什么?

​namespace:

​用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置

​group:

​Nacos中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串对配置集进行分组,从而区分 Data ID 相同的配置集

##### 5、nacos和eureka的异同有哪些?

​相同点:

​1、都支持服务注册和服务拉取

​2、都支持服务提供者心跳的方式做健康检测

​不同点:

​1、nacos支持服务端主动检测提供者状态:

​临时实例采用心跳模式,非临时实例采用主动检测模式(一般情况下都使用临时实例,主动检测消费的服务器资源较大,服务器压力大)

​2、临时实例心跳不正常会被剔除,非临时实例则不会被剔除

​3、nacos支持服务列表变更的消息推送模式,服务列表更新及时

​4、nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;eureka采用AP方式

##### 6、nacos的临时节点和非临时节点有什么区别?

​非临时实例挂掉后不会被nacos剔除,而是等待他重连

##### 7、nacos集群是属于AP还是CP?(AP或CP)

​nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式

​3)、Zookeeper

​1、Zookeeper的内部结构是什么?

​ZooKeeper维护和操作一个小型的数据节点,这些节点被称之为znode,采用类似于文件系统的层级树状结构进行管理

#### 2、使用Zookeeper作为分布式事务锁的原理是什么?

​锁分为两种:

​共享锁(读锁)和排他锁(写锁)

​读锁:

​当有一个线程获取读锁后,其他线程也可以获取读锁,但是在读锁没有完全被释放之前,其他线程不能获取写锁。

​写锁:

​当有一个线程获取写锁后,其他线程就无法获取读锁和写锁了。

​zookeeper 有一种节点类型叫做临时序号节点,它会按序号自增地创建临时节点,这正好可以作为分布式锁的实现工具。

​3、Zookeeper集群属于AP还是CP?(CP)

​CP

#### 3、配置中心

##### 1)、实现配置中心都可以使用哪些技术?

​1、Spring Cloud Config

​2、Apollo

​3、Nacos

##### 2)、使用nacos作为配置中心,如何实现热更新?

​1、在 @Value 注入的变量所在的类上添加注解 @RefreshScope

​2、使用 @ConfigurationProperties(prefix = "pattern") 注解

##### 3)、nacos作为配置中心,为什么需要用到bootstrap文件?

​得知nacos地址、配置文件id

##### 4)、远程配置文件和本地配置文件属性加载优先级是什么样的?

​远程 > 本地

​高优先级的会覆盖低优先级的重复的配置内容

##### 5)、使用配置中心的好处是什么?能解决什么问题?

​好处:

​将配置文件集中管理

​解决的问题:

​可以在配置变更时,及时通知微服务,实现配置的热更新

#### 4、服务保护

#### 1)、Hystrix

##### 1、hystrix是属于什么体系的技术?(SpringCloud)

​SpringCloud

##### 2、hystrix可以用来干嘛?(服务熔断降级)

​服务熔断降级

##### 3、hystrix默认的触发熔断策略是什么?(5分钟之内服务调用异常比例达到一半或者失败次数超过20次)

​5分钟之内服务调用异常比例达到一半或者失败次数超过20次

##### 4、hystrix的隔离是基于什么?(线程池隔离【低扇出】)

​线程池隔离(低扇出)

#### 2)、Sentinel

##### 1、sentinel可以用来干嘛?(限流、隔离、熔断、降级)

​限流、隔离、熔断、降级

##### 2、什么是微服务雪崩现象?如何解决微服务雪崩问题?

​微服务雪崩:

​微服务之间相互调用,因为调用链中的一个服务故障,引起整个链路都无法访问的情况

​解决方案:

​ 1、超时处理

​ 2、船壁模式(隔离)

​ 3、熔断,降级

#### 3、sentinel的限流模式有哪些?分别的运用场景是什么?

​ 1、直接:

​对当前资源限流、

​ 2、关联:

​ 比如用户支付时需要修改订单状态,同时用户要查询订单。

​查询和修改操作会争抢数据库锁,产生竞争。

​业务需求是优先支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。

​ 3、链路:

​ 阈值统计时,只统计从指定资源进入当前资源的请求,是对请求来源的限流

#### 4、sentinel的限流效果有哪些?分别的运用场景是什么?

​ 1、快速失败:

​QPS超过阈值时,拒绝新的请求

​ 2、warm up:

​ QPS超过阈值时,拒绝新的请求;

​ QPS阈值是逐渐提升的,可以避免冷启动时高并发导致服务宕机

​ 3、排队等待:

​ 请求会进入队列,按照阈值允许的时间间隔依次执行请求;如果请求预期等待时长大于超时时间,直接拒绝

​ 4、热点参数:

​ 分别统计参数值相同的请求,判断是否超过QPS阈值

#### 5、sentinel支持对热点参数进行限流吗?

​支持

#### 6、实现微服务调用隔离有两种方式(信号量隔离和线程池隔离),区别是什么?sentinel是使用的哪种?

​区别:

​1、信号量隔离:

​ 不创建线程池,而是计数器模式,记录业务使用的线程数量,达到信号量上限时,禁止新的请求。

​ 特点:基于计数器模式,简单,开销小;高扇出

​ 2、线程池隔离

​ 给每个服务调用业务分配一个线程池,利用线程池本身实现隔离效果

​ 特点:基于线程池模式,有额外开销,但隔离控制更强;低扇出

​ sentinel是使用的哪种:

​ 信号量隔离

#### 7、什么是熔断?熔断的原理是什么?什么时候会触发sentinel的熔断?断路器的三种状态是哪些?是怎样进行切换的?

​概念:

​熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),

​对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误

​熔断的原理:

​断路器

​什么时候会触发sentinel的熔断:

​1、慢调用

​2、异常比例或者异常数

​断路器的三种状态是哪些,是怎样进行切换的:

​1、关闭:默认就是关闭的

​2、半开:开启后到达指定(可配置)的时间,开启半开状态,尝试接收请求

​如果成功:状态置为关闭

​如果失败:状态置为开启

​3、开启:当触发配置的阈值,会开启

#### 8、什么是降级?如何实现降级?

​概念:

​当微服务调用失败,客户端走降级逻辑

​如何实现降级:

​1、FallBackClass

​2、FallBackFactoryClass(优势:可以获取服务端抛出的异常)

#### 9、sentinel授权规则是用来干什么的?

​授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式:

​1、白名单:来源(origin)在白名单内的调用者允许访问

​2、黑名单:来源(origin)在黑名单内的调用者不允许访问

#### 10、sentinel的规则持久化方式有哪些?一般使用哪种?

​sentinel的规则持久化方式:

​1、原始模式:Sentinel的默认模式,将规则保存在内存,重启服务会丢失。

​ 2、pull模式

​ 3、push模式

​ 一般使用哪种:

​ push模式

# 5、网关

#### 1)、网关有什么作用?在你们项目中用网关来干嘛了?

​1、权限控制:

​网关作为微服务入口,需要校验用户是是否有请求资格,如果没有则进行拦截。

​2、路由和负载均衡:

​一切请求都必须先经过gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由。当然路由的目标服务有多个时,还需要做负载均衡。

​3、限流:

​当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大。

#### 2)、网关的核心技术点有哪些?

​1、断言工厂

​2、过滤器工厂

​3、全局过滤器

​4、解决跨域

#### 3)、网关的路由是用来干嘛的?分为哪几种?

​路由的作用:

​一切请求都必须先经过gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务

​分为哪几种:

​静态路由、动态路由

#### 4)、网关的过滤器是用来干嘛的?分为哪几种?

​网关的过滤器作用:

​可以对进入网关的请求和微服务返回的响应做处理

​分为哪几种:

​1、路由过滤器

z 2、请求头过滤器

​3、默认过滤器

#### 5)、网关局部过滤器和全局过滤器的区别有哪些?

​局部过滤器:

​拦截经过网关的特定服务的请求

​全局过滤器:

​无差别拦截所有经过网关 的请求

#### 6)、网关中局部过滤器、默认过滤器、全局过滤器的执行顺序是什么?

​默认过滤器 > 局部过滤器 > 全局过滤器

#### 7)、加入网关后,访问一个链接,你们项目的执行流程是什么?

​1、客户端发送请求

​2、进行权限校验,如果是登录或者注册操作则直接放行

​3、校验通过生成该用户的token,校验失败拦截

​4、路由匹配到某个微服务

#### 8)、定义全局过滤器需要实现哪几个接口?

​GlobalFilter

# 6、分布式事务

#### 1)、什么是本地事务?什么是分布式事务?

​本地事务:

​无需跨越多个服务或者数据源的单体事务,一般由spring控制即可(声明式事务处理:AOP)

​分布式事务:

​指一个业务跨越多个服务或者数据源,每个事务叫做分支事务,要保证所有分支事务要么成功,要么失败

#### 2)、什么是CAP定理?为什么必须保证P?为什么在保证P的前提下只能保证C或者A其中一个?

​CAP定理:

​‘’Consistency(一致性)、Availability(可用性)、Partition tolerance (分区容错性),这三个指标不可能同时做到

​为什么必须保证P:

​在分布式系统中,系统间的网络不能100%保证健康,一定会有故障的时候,而服务有必须对外保证服务。

​ 因此Partition Tolerance不可避免

​为什么在保证P的前提下只能保证C或者A其中一个:

​如果此时要保证一致性,就必须等待网络恢复,完成数据同步后,整个集群才对外提供服务,服务处于阻塞状态,不可用。

​如果此时要保证可用性,就不能等待网络恢复,那 node01、node02、node03 之间就会出现数据不一致。

#### 3)、什么是BASE理论?

​BASE理论是对CAP的一种解决思路,包含三个思想:

​1、Basically Available(基本可用):

​分布式系统在出现故障时,允许损失部分可用性,即保证核心可用

​2、Soft State(软状态):

​在一定时间内,允许出现中间状态,比如临时的不一致状态

​3、Eventually Consistent(最终一致性):

​虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致

#### 4)、seata解决分布式事务的三个角色以及分别的作用什么?

​1、TC (Transaction Coordinator) - 事务协调者:

​维护全局和分支事务的状态,协调全局事务提交或回滚

​ 2、TM (Transaction Manager) - 事务管理器:

​ 定义全局事务的范围、开始全局事务、提交或回滚全局事务

​ 3、RM (Resource Manager) - 资源管理器:

​ 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚

#### 5)、seata解决分布式事务的四种模式

​1.XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入;

​2.AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式;

​3.TCC模式:最终一致的分阶段事务模式,有业务代码侵入;

​4.SAGA模式:长事务模式,有业务侵入。

#### 6)、XA模式特点、原理以及应用场景?优缺点有哪些?

​特点:

​强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入 (CP)

​原理:1)、第一阶段:

​1、TM:

​1、开启全局事务

​2、调用分支事务

​2、TC:

​接收开启全局事务的请求

​3、RM:

​1、将分支事务注册到TC服务

​2、执行本地sql,但是,重点:不提交事务!!!!

​3、将本地事务状态报告至TC服务

​2)、第二阶段:

​1、TM:

​等第一阶段所有分支事务执行完,发起提交/回滚全局事务的命令

​2、TC:

​接收全局事务提交/回滚请求,核查所有分支事务的状态,对RM发起提交/回滚的命令

​3、RM:

​提交或者回滚当前的分支事务(依赖于数据库)

​优点:

​强一致性、无代码侵入、实现简单

​缺点:

​强依赖于关系型数据库实现回滚、性能比较差

#### 7)、AT模式特点、原理以及应用场景?优缺点有哪些?有可能会出现什么问题?如何解决?

​特点:

​同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷,属于最终一致(AP)

​原理:

​1)、第一阶段:

​1、TM:

​1、开启全局事务

​2、调用分支事务

​2、TC:

​接收开启全局事务的请求

​3、RM:

​1、将分支事务注册到TC服务

​2、执行本地sql,重点:提交事务!!!!

​3、执行sql前后,生成快照:undo_log

​4、将本地事务状态报告至TC服务

​2)、第二阶段:

​1、TM:

​等第一阶段所有分支事务执行完,发起提交/回滚全局事务的命令

​2、TC:

​接收全局事务提交/回滚请求,核查所有分支事务的状态,对RM发起提交/回滚的命令

​3、RM:

​提交或者回滚当前的分支事务(依赖于undo_log快照数据)

​提交:删除快照数据

​回滚:根据快照进行数据恢复

​优点:

​1、一阶段完成直接提交事务,释放数据库资源,性能比较好

​2、利用全局锁实现读写隔离

​3、没有代码侵入,框架自动完成回滚和提交

​缺点:

​1、两阶段之间属于软状态,属于最终一致

​2、框架的快照功能会影响性能,但比XA模式要好很多

​可能出现的问题:

​脏写:当全局事务1提交修改的数据后,此时全局事务2又过来修改了这条数据

​ 后续阶段二全局事务1需要利用快照进行回滚,将全局事务2的所有修改进行了覆盖

​解决方案:

​seata内部提供了全局锁的概念(需要在seata server新增一张全局锁的表)

​但是全局锁有可能导致死锁(内部通过限制获取全局锁的次数来解决:30次/10ms)

#### 8)、重点:TCC模式特点、原理以及应用场景?优缺点有哪些?有可能会出现什么问题?什么是空回滚和业务悬挂,如何解决?

​特点:

​与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。属于最终一致(AP)

​原理:

​1、T: Try,进行资源的检测和预留

​2、C:Confirm,对资源进行确认操作(业务执行和提交)

​3、C:Cancle,对资源进行回滚操作(预留资源的释放)

​优点:

​1、一阶段完成直接提交事务,释放数据库资源,性能好

​2、相比AT模型,无需生成快照,无需使用全局锁,性能最强

​3、不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库

​缺点:

​1、有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦

​2、软状态,事务是最终一致

​3、需要考虑Confirm和Cancel的失败情况,做好幂等处理

​可能出现的问题:

​1、空回滚

​问题描述:

​当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。

​在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,则就是空回滚。

​解决办法:

​执行cancel操作时,应当判断try是否已经执行,如果尚未执行,则应该空回滚。

​2、业务悬挂

​问题描述:

​对于已经空回滚的业务,之前被阻塞的try操作恢复,继续执行try,就永远不可能

​ confirm或cancel ,事务一直处于中间状态,这就是业务悬挂。

​解决办法:

​执行try操作时,应当判断cancel是否已经执行过了,如果已经执行,应当阻止空回滚后的try操作,避免悬挂

#### 9)、SAGA模式特点、原理以及应用场景?优缺点有哪些?

​概念:

​Saga 模式是 Seata 即将开源的长事务解决方案,将由蚂蚁金服主要贡献。

​原理:

​在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。

​分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。

​如果任何一个正向操作执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,

​使分布式事务回到初始状态。

​优点:

​1、事务参与者可以基于事件驱动实现异步调用,吞吐高

​2、一阶段直接提交事务,无锁,性能好

​3、不用编写TCC中的三个阶段,实现简单

​缺点:

​1、软状态持续时间不确定,时效性差

​2、没有锁,没有事务隔离,会有脏写

## 九、Docker

#### 1、什么是Docker?优点是什么?有哪些核心概念?

​概念:

​Docker是一个快速交付应用、运行应用的技术

​优点:

​1、可以将程序及其依赖、运行环境一起打包为一个镜像,可以迁移到任意Linux操作系统

​2、运行时利用沙箱机制形成隔离容器,各个应用互不干扰

​3、启动、移除都可以通过一行命令完成,方便快捷

​核心概念:

​解决大型项目依赖关系复杂,不同组件依赖的兼容性问题:

​1、Docker允许开发中将应用、依赖、函数库、配置一起打包,形成可移植镜像

​2、Docker应用运行在容器中,使用沙箱机制,相互隔离

​解决开发、测试、生产环境有差异的问题:

​Docker镜像中包含完整运行环境,包括系统函数库,仅依赖系统的Linux内核,因此可以在任意Linux操作系统上运行

#### 2、镜像操作命令有哪些?

​拉取、推送、查看、查看所有、删除、删除所有、制作镜像、导出镜像、加载镜像

#### 3、容器操作命令有哪些?

​查看所有、查看正在运行的容器、删除、强制删除、创建容器、创建并运行、

​启动容器、停止容器、重启容器、暂停容器、恢复容器、进入容器

#### 4、数据卷操作命令有哪些?

​创建数据卷、查看单个数据卷详情、查看数据卷列表、删除数据卷、删除未使用的数据卷、创建容器时挂载数据卷

#### 5、docker如何自定义镜像?docker file的语法是什么样的?

​自定义镜像:

​1、准备基础镜像和tar包

​2、创建Dockerfile

​3、使用Dockerfile创建镜像

​docker file的语法:

​保留字指令要大写,后边有空格,后边必须有内容。 比如:FROM scratch

```java

指令从上往下依次执行

```

#### 6、docker compose是干嘛的?语法是什么样的?

​作用:

​调用docker服务的API负责实现对docker容器集群的快速编排,即通过一个单独的yaml文件,来定义一组相关的容器来为一个项目服务

​语法:

​一份标准配置文件应该包含 version、services、networks 三大部分,其中最关键的就是 services 和 networks 两个部分

## 十、ElasticSearch

#### 1、什么是ES?由什么语言编写?和Lunce的关系?什么是ELK?

​概念:

​1)、是一款分布式、高性能、高扩展,支持海量数据分析、搜索、计算的搜索引擎

​2)、基于Java语言编写,发起的请求时基于Json风格的符合RestFull风格的DSL语句

​3)、前身Lucene诞生于1999年,2004年变为了compass,2010年重构成了现在的ES

​4)、ELK:是一个围绕ElasticSearch的技术栈,包含:ElasticSearch、Logstatch、Kibana,最新版本7.16.3

​5)、Solr:是ES的一款竞品,2016被ES反超,主流成为ES

​由什么语言编写:

​Java

​和Lunce的关系:

​前身Lucene诞生于1999年,2004年变为了compass,2010年重构成了现在的ES

​什么是ELK:

​是一个围绕ElasticSearch的技术栈,包含:ElasticSearch、Logstatch、Kibana,最新版本7.16.3

#### 2、ES的核心概念有哪些?什么是索引?什么是文档?文档格式是什么?什么是映射?什么是DSL?

​核心概念:

​1)、倒排索引

​对文档进行合理化的分词,形成一个不重复的词条列表,每一个词条对应一个文档id集合,将来根据文档分成词条找id,再根据id找到相应的文档(涉及到两次的Btree查询)

​2)、索引 -- index

​同一类型文档的集合,相当于mysql的表

​3)、映射 -- mapping

​对索引结构的约束,相当于mysql的schema(约束)(表结构)

​4)、文档 -- document

​Json格式的数据,相当于mysql的row(行)

​5)、字段 -- field

​一个个的字段,相当于mysql的列

​6)、DSL语句

​Json风格的符合restful风格的请求语句

#### 3、什么是倒排索引?倒排索引建立过程?

​倒排索引:

​对文档进行合理化的分词,形成一个不重复的词条列表,每一个词条对应一个文档id集合,将来根据文档分成词条找id,再根据id找到相应的文档(涉及到两次的Btree查询)

​倒排索引建立过程:

​1、将每一个文档的数据利用算法分词,得到一个个词条

​2、创建表,每行数据包括词条、词条所在文档id、位置等信息

​3、因为词条唯一性,可以给词条创建索引,例如hash表结构索引

#### 4、ES有哪些数据类型?keyword和text有什么区别?

​数据类型:

​1、字符串:

​text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)

​2、数值:

​long、integer、short、byte、double、float、

​3、布尔:

​boolean

​4、日期:

​date

​5、对象:

​object

​keyword和text有什么区别:

​text可分词,keyword不可分词

#### 5、重要:说说用户输入框输入查询条件 进行ES搜索的底层原理过程

​1、如果用户输入条件“华为手机”进行搜索。

​2、对用户输入内容分词,得到词条:“华为”、“手机”。

​3、拿着词条在倒排索引中查找,可以得到包含词条的文档id:1、2、3。

​4、拿着文档id到正向索引中查找具体文档。

#### 6、ES分词器适合在什么字段上使用?分词器在ES中的使用场景有哪些?(建立倒排索引时对文档分词和用户搜索时对搜索条件分词)

​建立倒排索引时对文档分词和用户搜索时对搜索条件分词

#### 7、你们分词器用的哪种?为什么要自定义拼音分词器?为什么搜索时不能用拼音分词器?

​用的哪种:

​IK分词器

​为什么要自定义拼音分词器:

​要实现根据字母做补全,就必须对文档按照拼音分词。

​默认的拼音分词器会将每个汉字单独分为拼音,而我们希望的是每个词条形成一组拼音,需要对拼音分词器做个性化定制,形成自定义分词器

​为什么搜索时不能用拼音分词器:

​为了避免搜索到同音字,搜索时不要使用拼音分词器

#### 8、ES有哪些查询类型,分别用在什么场景?如何实现复合查询?要给指定的数据进行加分如何实现?

​查询类型:

​1、查询所有:

​查询出所有数据,一般测试用。例如:match_all

​2、全文检索(full text)查询:

​利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:

​match_query

​multi_match_query

​3、精确查询:

​根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如:

​ids

​range

​term

​4、地理(geo)查询:

​根据经纬度查询。例如:

​geo_distance

​eo_bounding_box

​5、复合(compound)查询:

​复合查询可以将上述各种查询条件组合起来,合并查询条件。例如:

​bool

​function_score

​如何实现复合查询:

​1、根据原始条件查询搜索文档,并且计算相关性算分,称为原始算分(query score)

​2、根据过滤条件,过滤文档

​3、符合过滤条件的文档,基于算分函数运算,得到函数算分(function score)

​4、将原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终结果,作为相关性算分。

​给指定的数据进行加分如何实现:

​算分函数:

​可以简单粗暴,直接给固定的算分结果,weight

#### 9、给指定的数据进行加分如何实现?如何实现高亮?

​给指定的数据进行加分如何实现:

​1、排序

​2、分页

​3、高亮

​如何实现高亮:

​1、给文档中的所有关键字都添加一个标签,例如<em>标签

​2、页面给<em>标签编写CSS样式

#### 10、ES有哪些聚合查询?

​1、桶(Bucket)聚合:

​用来对文档做分组

​2、度量(Metric)聚合:

​用以计算一些值,比如:最大值、最小值、平均值等

​3、管道(pipeline)聚合:

​其它聚合的结果为基础做聚合

#### 11、ES如何实现自动补全查询

​1、修改索引库结构,设置自定义拼音分词器

​2、修改索引库的需要补全的属性,使用自定义分词器

​3、索引库添加一个新字段suggestion,类型为completion类型,使用自定义的分词器

​4、给实体类doc添加suggestion字段

​5、重新导入数据到索引库

#### 12、如何自定义分词器?

​1、创建索引库时,在settings中配置,可以包含三部分

​2、character filter:

​在tokenizer之前对文本进行处理。例如删除字符、替换字符

​3、tokenizer:

​将文本按照一定的规则切割成词条(term)。例如keyword,就是不分词;还有ik_smart

​4、filter:

​将tokenizer输出的词条做进一步处理。例如大小写转换、同义词处理、拼音处理等

#### 13、如何实现es与mysql的数据同步?

​1、同步调用:

​1、ES微服务对外提供接口,用来修改elasticsearch中的数据

​2、操作数据库微服务在完成数据库操作后,直接调用微服务提供的接口,

​2、异步通知:

​1、操作数据库微服务对mysql数据库数据完成增、删、改后,发送MQ消息

​2、ES微服务监听MQ,接收到消息后完成elasticsearch数据修改

​3、监听binlog

​1、给mysql开启binlog功能

​2、mysql完成增、删、改操作都会记录在binlog中

​3、ES微服务基于canal监听binlog变化,实时更新elasticsearch中的内容

#### 14、es集群节点有哪些类型?分别的职责是什么?

​1、备选主节点(master eligible):

​主节点可以管理和记录集群状态、决定分片在哪个节点、处理创建和删除索引库的请求

​2、数据节点(data):

​存储数据、搜查、聚合、CRUD

​3、ingest:

​数据存储之前的预处理

​4、coordinating:

​路由请求到其他节点,合并其他节点处理的结果,返回给用户

#### 15、什么是es脑裂问题?

​1、一个集群中,主节点与其它节点失联

​2、此时,node2和node3认为node1宕机,就会重新选主

​3、当node3当选后,集群继续对外提供服务,node2和node3自成集群,node1自成集群,两个集群数据不同步,出现数据差异

​4、当网络恢复后,因为集群中有两个master节点,集群状态的不一致,出现脑裂的情况

## 十一、压测、高并发性能优化

​1、jemiter压测工具使用

​2、多级缓存(nginx共享字典、redis缓存、tomcat进程缓存)

​3、数据库主从读写分离(mycat)

​4、发布:

​开发环境、测试环境用的shell脚本自动发布(包括从git上拉取代码、打包编译、启动)

​预上线、线上环境用的jenkins持续集成

​5、提交代码之前需要做什么?

# get和post有什么区别

一、功能不同

1、get是从服务器上获取数据。

2、post是向服务器传送数据。

二、过程不zhi同

1、get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。

2、post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。

三、获取值不同

1、对于get方式,服务器端用Request.QueryString获取变量的值。

2、对于post方式,服务器端用Request.Form获取提交的数据。

四、传送数据量不同

1、get传送的数据量较小,不能大于2KB。

2、post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。

五、安全性不同

1、get安全性非常低。

2、post安全性较高。

# Object类下面有那些方法

hashCode,equals,clone,toString,notify,notifyAll,wait,finalize。

# AtomicInteger原子操作类

AtomicInteger底层实现采用CAS操作,调用的是Unsafe的compareAndSwapInt方法。

CAS全称是CompareAndSwap,它是一条CPU并发原语。用来判断内存某个位置的值是否为预期值,如果是则改为更新的值,这个过程是原子的。

AtomicInteger之所以能保证原子性是依赖于UnSafe类,这个类是Java最底层的类之一,里面都是很屌的native方法,都是其他语言写的,咱看不见,Unsafe类可以执行以下几种操作:

分配内存,释放内存

可以定位对象的属性在内存中的位置,可以修改对象的属性值。使用objectFieldOffset方法

挂起和恢复线程,被封装在LockSupport类中供使用CAS

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一. Java基础部分 7 1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 7 2、Java有没有goto? 7 3、说说&和&&的区别。 8 4、在JAVA中如何跳出当前的多重嵌套循环? 8 5、switch语句能否作用在byte上,能否作用在long上,能否作用在String上? 9 6、short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错? 9 7、char型变量中能不能存贮一个中文汉字?为什么? 9 8、用最有效率的方法算出2乘以8等於几? 9 9、请设计一个一百亿的计算器 9 10、使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变? 11 11、"=="和equals方法究竟有什么区别? 11 12、静态变量和实例变量的区别? 12 13、是否可以从一个static方法内部发出对非static方法的调用? 12 14、Integer与int的区别 13 15、Math.round(11.5)等於多少? Math.round(-11.5)等於多少? 13 16、下面的代码有什么不妥之处? 13 17、请说出作用域public,private,protected,以及不写时的区别 13 18、Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型? 14 19、构造器Constructor是否可被override? 15 20、接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承具体类(concrete class)? 抽象类中是否可以有静态的main方法? 15 21、写clone()方法时,通常都有一行代码,是什么? 15 22、面向对象的特征有哪些方面 15 23、java中实现多态的机制是什么? 17 24、abstract class和interface有什么区别? 17 25、abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized? 18 26、什么是内部类?Static Nested Class 和 Inner Class的不同。 19 27、内部类可以引用它的包含类的成员吗?有没有什么限制? 20 28、Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)? 21 29、super.getClass()方法调用 21 30、String是最基本的数据类型吗? 22 31、String s = "Hello";s = s + " world!";这两行代码执行后,原始的String对象中的内容到底变了没有? 22 32、是否可以继承String类? 23 33、String s = new String("xyz");创建了几个String Object? 二者之间有什么区别? 23 34、String 和StringBuffer的区别 23 35、如何把一段逗号分割的字符串转换成一个数组? 24 36、数组有没有length()这个方法? String有没有length()这个方法? 24 37、下面这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d"; 24 38、try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后? 25 39、下面的程序代码输出的结果是多少? 25 40、final, finally, finalize的区别。 27 41、运行时异常与一般异常有何异同? 27 42、error和exception有什么区别? 28 43、Java中的异常处理机制的简单原理和应用。 28 44、请写出你最常见到的5个runtime exception。 28 45、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 29 46、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用? 29 47、sleep() 和 wait() 有什么区别? 30 48、同步和异步有何异同,在什么情况下分别使用他们?举例说明。 32 49. 下面两个方法同步吗?(自己发明) 33 50、多线程有几种实现方法?同步有几种实现方法? 33 51、启动一个线程是用run()还是start()? . 33 52、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 33 53、线程的基本概念、线程的基本状态以及状态之间的关系 34 54、简述synchronized和java.util.concurrent.locks.Lock的异同 ? 34 55、设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。 36 56、子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次,请写出程序。 38 57、介绍Collection框架的结构 43 58、Collection框架中实现比较要实现什么接口 43 59、ArrayList和Vector的区别 44 60、HashMap和Hashtable的区别 44 61、List 和 Map 区别? 45 62、List, Set, Map是否继承自Collection接口? 45 63、List、Map、Set三个接口,存取元素时,各有什么特点? 45 64、说出ArrayList,Vector, LinkedList的存储性能和特性 46 65、去掉一个Vector集合中重复的元素 46 66、Collection 和 Collections的区别。 47 67、Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别? 47 68、你所知道的集合类都有哪些?主要方法? 47 69、两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对? 48 70、TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常! 48 71、说出一些常用的类,包,接口,请各举5个 49 72、java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类? 49 73、字节流与字符流的区别 50 74、什么是java序列化,如何实现java序列化?或者请解释Serializable接口的作用。 51 75、描述一下JVM加载class文件的原理机制? 52 76、heap和stack有什么区别。 52 77、GC是什么? 为什么要有GC? 52 78、垃圾回收的优点和原理。并考虑2种回收机制。 52 79、垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收? 52 80、什么时候用assert。 53 81、java中会存在内存泄漏吗,请简单描述。 53 82、能不能自己写个类,也叫java.lang.String? 57 83. Java代码查错 57 二. 算法与编程 61 1、编写一个程序,将a.txt文件中的单词与b.txt文件中的单词交替合并到c.txt文件中,a.txt文件中的单词用回车符分隔,b.txt文件中用回车或空格进行分隔。 61 2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉的半个”。 65 4、有一个字符串,其中包含中文字符、英文字符和数字字符,请统计和打印出各个字符的个数。 65 5、说明生活中遇到的二叉树,用java实现二叉树 66 6、从类似如下的文本文件中读取出所有的姓名,并打印出重复的姓名和重复的次数,并按重复次数排序: 71 7、写一个Singleton出来。 75 8、递归算法题1 77 9、递归算法题2 78 10、排序都有哪几种方法?请列举。用JAVA实现一个快速排序。 79 11、有数组a[n],用java代码将数组元素顺序颠倒 80 12.金额转换,阿拉伯数字的金额转换成中国传统的形式如:(¥1011)->(一千零一拾一元整)输出。 81 三. html&JavaScript&ajax部分 82 1. 判断第二个日期比第一个日期大 82 2. 用table显示n条记录,每3行换一次颜色,即1,2,3用红色字体,4,5,6用绿色字体,7,8,9用红颜色字体。 83 3、HTML 的 form 提交之前如何验证数值文本框的内容全部为数字? 否则的话提示用户并终止提交? 84 4、请写出用于校验HTML文本框中输入的内容全部为数字的javascript代码 84 5、说说你用过那些ajax技术和框架,说说它们的区别 85 四. Java web部分 85 1、Tomcat的优化经验 85 2、HTTP请求的GET与POST方式的区别 85 3、解释一下什么是servlet; 85 4、说一说Servlet的生命周期? 86 5、Servlet的基本架构 86 6、SERVLET API中forward() 与redirect()的区别? 86 7、什么情况下调用doGet()和doPost()? 86 8、Request对象的主要方法: 87 9、forward 和redirect的区别 87 10、request.getAttribute() 和 request.getParameter() 有何区别? 88 11. jsp有哪些内置对象?作用分别是什么? 分别有什么方法? 88 12. jsp有哪些动作?作用分别是什么? 88 13、JSP的常用指令 89 14. JSP中动态INCLUDE与静态INCLUDE的区别? 89 15、两种跳转方式分别是什么?有什么区别? 89 16、页面间对象传递的方法 89 17、JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么? 90 18、MVC的各个部分都有那些技术来实现?如何实现? 90 19、我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串? 90 20.现在输入n个数字,以逗号,分开;然后可选择升或者降序排序;按提交键就在另一页面显示按什么排序,结果为,提供reset 91 五. 数据库部分 91 1、用两种方式根据部门号从高到低,工资从低到高列出每个员工的信息。 91 2、列出各个部门中工资高于本部门的平均工资的员工数和部门号,并按部门号排序 91 3、存储过程与触发器必须讲,经常被面试到? 92 4、数据库三范式是什么? 94 5、说出一些数据库优化方面的经验? 95 6、union和union all有什么不同? 96 7.分页语句 97 8.用一条SQL语句 查询出每门课都大于80分的学生姓名 100 9.所有部门之间的比赛组合 100 10.每个月份的发生额都比101科目多的科目 101 11.统计每年每月的信息 102 12.显示文章标题,发帖人、最后回复时间 103 13.删除除了id号不同,其他都相同的学生冗余信息 104 14.航空网的几个航班查询题: 104 15.查出比经理薪水还高的员工信息: 105 16、求出小于45岁的各个老师所带的大于12岁的学生人数 106 17.求出发帖最多的人: 107 18、一个用户表中有一个积分字段,假如数据库中有100多万个用户,若要在每年第一天凌晨将积分清零,你将考虑什么,你将想什么办法解决? 107 19、一个用户具有多个角色,请查询出该表中具有该用户的所有角色的其他用户。 108 20. xxx公司的sql面试 108 21、注册Jdbc驱动程序的三种方式 109 22、用JDBC如何调用存储过程 109 23、JDBC中的PreparedStatement相比Statement的好处 110 24. 写一个用jdbc连接并访问oracle数据的程序代码 111 25、Class.forName的作用?为什么要用? 111 26、大数据量下的分页解决方法。 111 27、用 JDBC 查询学生成绩单, 把主要代码写出来(考试概率极大). 112 28、这段代码有什么不足之处? 112 29、说出数据连接池的工作机制是什么? 113 30、为什么要用 ORM? 和 JDBC 有何不一样? 113 六. XML部分 113 1、xml有哪些解析技术?区别是什么? 113 2、你在项目中用到了xml技术的哪些方面?如何实现的? 114 3、用jdom解析xml文件时如何解决中文问题?如何解析? 114 4、编程用JAVA解析XML的方式. 115 5、XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式? 117 七. 流行的框架与新技术 117 1、谈谈你对Struts的理解。 117 2、谈谈你对Hibernate的理解。 118 3、AOP的作用。 118 4、你对Spring的理解。 118 5、谈谈Struts中的Action servlet。 120 6、Struts优缺点 优点: 1. 实现MVC模式,结构清晰,使开发者只关注业务逻辑的实现. 120 7、STRUTS的应用(如STRUTS架构) 121 8、说说struts1与struts2的区别。 121 9、hibernate中的update()和saveOrUpdate()的区别,session的load()和get()的区别。 122 10、简述 Hibernate 和 JDBC 的优缺点? 如何书写一个 one to many 配置文件. 122 11、iBatis与Hibernate有什么不同? 122 12、写Hibernate的一对多和多对一双向关联的orm配置? 122 9、hibernate的inverse属性的作用? 122 13、在DAO中如何体现DAO设计模式? 123 14、spring+Hibernate中委托方案怎么配置? 123 15、spring+Hibernate中委托方案怎么配置? 123 16. hibernate进行多表查询每个表中各取几个字段,也就是说查询出来的结果集没有一个实体类与之对应如何解决; 123 17.介绍一下Hibernate的二级缓存 123 18、Spring 的依赖注入是什么意思? 给一个 Bean 的 message 属性, 字符串类型, 注入值为 "Hello" 的 XML 配置文件该怎么写? 125 19、Jdo是什么? 125 20、什么是spring的IOC AOP 126 21、STRUTS的工作流程! 126 22、spring 与EJB的区别!! 126 八. 软件工程与设计模式 126 1、UML方面 126 2、j2ee常用的设计模式?说明工厂模式。 126 3、开发中都用到了那些设计模式?用在什么场合? 127 九. j2ee部分 127 1、BS与CS的联系与区别。 127 2、应用服务器与WEB SERVER的区别? 128 3、应用服务器有那些? 128 4、J2EE是什么? 128 5、J2EE是技术还是平台还是框架? 什么是J2EE 128 6、请对以下在J2EE中常用的名词进行解释(或简单描述) 129 7、如何给weblogic指定大小的内存? 129 8、如何设定的weblogic的热启动模式(开发模式)与产品发布模式? 129 9、如何启动时不需输入用户名与密码? 130 10、在weblogic管理制台中对一个应用域(或者说是一个网站,Domain)进行jms及ejb或连接池等相关信息进行配置后,实际保存在什么文件中? 130 11、说说weblogic中一个Domain的缺省目录结构?比如要将一个简单的helloWorld.jsp放入何目录下,然的在浏览器上就可打入http://主机:端口号//helloword.jsp就可以看到运行结果了? 又比如这其中用到了一个自己写的javaBean该如何办? 130 12、在weblogic中发布ejb需涉及到哪些配置文件 130 13、如何在weblogic中进行ssl配置与客户端的认证配置或说说j2ee(标准)进行ssl的配置? 130 14、如何查看在weblogic中已经发布的EJB? 131 十. EBJ部分 131 1、EJB是基于哪些技术实现的?并说出SessionBean和EntityBean的区别,StatefulBean和StatelessBean的区别。 131 2、简要讲一下 EJB 的 7 个 Transaction Level? 131 3、EJB与JAVA BEAN的区别? 131 4、EJB包括(SessionBean,EntityBean)说出他们的生命周期,及如何管理事务的? 132 5、EJB容器提供的服务 132 6、EJB的激活机制 132 7、EJB的几种类型 132 8、客服端调用EJB对象的几个基本步骤 133 十一. webservice部分 133 1、WEB SERVICE名词解释。JSWDL开发包的介绍。JAXP、JAXM的解释。SOAP、UDDI,WSDL解释。 133 2、CORBA是什么?用途是什么? 133 3. Linux 134 4、LINUX下线程,GDI类的解释。 134 5. 问得稀里糊涂的题 134 6、四种会话跟踪技术 134 7、简述逻辑操作(&,|,^)与条件操作(&&,||)的区别。 134 十二. 其他 134 1、请用英文简单介绍一下自己. 134 2、请把 http://tomcat.apache.org/ 首页的这一段话用中文翻译一下? 135 3、美资软件公司JAVA工程师电话面试题目 135

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值