导图社区 JAVA面试题-2
这是一篇关于JAVA面试题-2的思维导图,有助于求职者可以系统地回顾和巩固JAVA的基础知识。干货满满,有需要的朋友赶紧收藏吧!
编辑于2024-03-07 14:55:06JAVA面试题
JAVA基础
基础语法
01-什么是面向对象编程 ?
面向对象基本思想是使用对象、类、继承、封装、多态等基本概念来进行程序设计。从现实世界中客观存在的事物(即对象)出发来构造软件系统,并且在系统构造中尽可能运用人类的自然思维方式。
02-面向对象三大特性
封装 : 将抽取出来的事务的属性和行为封装到类中 继承 : 子类继承父类 , 可以实现代码的抽取和复用 , java中只存在单继承 , 但是可以多实现 多态:父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高了程序的拓展性。
03-什么是多态机制 ?
所谓多态就是父类引用指向子类实现 , 例如 : Person p = new Man(); Java实现多态有三个必要条件:继承、重写、向上转型
04-Jdk和Jre和JVM的区别
JDK : Jdk还包括了一些Jre之外的东西 ,就是这些东西帮我们编译Java代码的, 还有就是监控Jvm 的一些工具 Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也 包括了JRE。所以安装了JDK,就无需再单独安装JRE了 Jre : 大部分都是 C 和 C++ 语言编写的,他是我们在编译java时所需要的基础的类库 Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等
05-Java的基本数据类型有哪些 ?
4类8种 整形 : byte 1字节 short 2字节 int 4字节 long 8字节 浮点型 float 4字节 dubbo 8字节 字符型 char 2字节 布尔型 boolean 1字节
06-什么是方法签名 ?
方法声明的两个组件构成了方法签名 - 方法的名称和参数类型
07-JAVA中访问修饰符有哪些 ?
private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类) default (即缺省,什么也不写,不使用任何关键字): 在同一包内可见,不使用任何修饰符。 使用对象:类、接口、变量、方法。 protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类 (外部类)。 public : 对所有类可见。使用对象:类、接口、变量、方法
08-&和&&的区别 ?
&&之称为短路运算,如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算 & : 如果&&左边的表达式的值是 false右边仍然会参与运算
09-final 有什么用 ?
被final修饰的类不可以被继承 被final修饰的方法不可以被重写 被final修饰的变量不可以被改变 注意 : 被final修饰不可变的是变量的引用,而不是引用指向的内容, 引用指向的内容是可以改变的
10-final finally finalize区别 ?
final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、 修饰变量表 示该变量是一个常量不能被重新赋值。 finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法 finally代码块 中,表示不管是否出现异常,该代码块都会执行 finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾 回收器来调 用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾
11-this和super关键字
this 是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针。 super 可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父 类。 super()和this()类似,区别是super()在子类中调用父类的构造方法,this()在本类内调用本类的其它构造方法 this和super不能同时出现在一个构造函数里面 , super()和this()均需放在构造方法内第一行。
12-break ,continue ,return 的区别及作用 ?
break 跳出总上一层循环,不再执行循环(结束当前的循环体) continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件) return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)
13-抽象类能使用 final 修饰吗?
不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生 矛盾,所以 final 不能修饰抽象类
14-static修饰的方法能调用非static修饰的变量和方法吗 ?
static : 代表静态, 类被加载的时候就会被初始化 静态方法只能访问静态修饰的方法和遍量 非static可以调用成员方法和静态方法
15-局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final?
是因为生命周期不一致, 局部变量直接存储在栈中,当方法执行 结束后,非final的局部变量就被销毁。而局部内部类对局部变量的引用依然存在,如果局部内部 类要调用局部变量时,就会出错。加了final,可以确保局部内部类使用的变量与外层的局部变量 区分开,解决了这个问题
16-重载(Overload)和重写(Override)的区别
重载: 发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与 方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分 重写: 发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于 父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中 就能是重写。
17-构造器(constructor)是否可被重写(override)
构造器不能被继承,因此不能被重写,但可以被重载。
18- == 和 equals 的区别是什么
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数 据类型 == 比较的是值,引用数据类型 == 比较的是内存地址) equals() : 它的作用也是判断两个对象是否相等
19-hashCode 方法的作用
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作 用是确定该对象在哈希表中的索引位置 不同的对象可能会有相同的hashcode , 在比较一个对象是否相等时 , 首先需要比较对象的hashcode是否相等, 其次要比较equal是否相等 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖 hashCode() 的默认行为是对堆上的对象产生独特值。 如果没有重写 hashCode(),则该 class 的两个对象 无论如何都不会相等
20-什么是反射机制?
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任 意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法 的功能称为java语言的反射机制。
21-反射机制优缺点
优点: 运行期类型的判断,动态加载类,提高代码灵活度。 缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要 慢很多
22-反射机制的应用场景有哪些?
我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节 码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/SpringMVC / Mybatis 等框架也大 量使用到了反射机制
23-Java获取反射的三种方法
1.通过new对象实现反射机制 2.通过路径实现反射机制 3.通过类名实现反射机制
24-Java中创建对象的方式有哪些 ?
使用new关键字 使用Class类的newInstance方法 使用Constructor类的newInstance方法 使用clone方法 使用反序列化
25-String s = new String(“xyz”);创建了几个字符串对象
两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象。
26- String str="i"与 String str=new String(“i”)一样吗?
不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中; 而 String str=new String(“i”) 则会被分到堆内存中。
27-String和StringBuffer、StringBuilder的区别是什么?
可变性 : String类中使用字符数组保存字符串,private final char value[],所以string对象是不可变 的。StringBuilder与StringBuffer都是使用数组保存字符串,char[] value,这两种对象都是可变的。 线程安全性 : String中的对象是不可变的,也就可以理解为常量,线程安全。StringBuffer对方法加了同步锁或者对调用的方法加了同 步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。 性能 : 每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对 象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引 用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升。
28-Integer a= 127 与 Integer b = 127相等吗 ?
如果整型字面量的值在-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的 Integer对象,超过范围 a1==b1的结果是false
数据结构
01-什么是链表 ?
链表是可以将物理地址上不连续的数据连接起来,通过指针来对物理地址进行操作,实现增删改查 等功能。 链表大致分为单链表和双向链表 单链表: 每个节点包含两部分,一部分存放数据变量的data,另一部分是指向下一节点的next指 针 双向链表: 除了包含单链表的部分,还增加的pre前一个节点的指针
02-链表的优点 ?
链表的优点 插入删除速度快(因为有next指针指向其下一个节点,通过改变指针的指向可以方便的增加 删除元素) 内存利用率高,不会浪费内存(可以使用内存中细小的不连续空间(大于node节点的大 小),并且在需要空间的时候才创建空间) 大小没有固定,拓展很灵活。 链表的缺点 : 不能随机查找,必须从第一个开始遍历,查找效率低
03-什么是红黑树 ?
红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须满足下面性质: 性质1:每个节点要么是黑色,要么是红色。 性质2:根节点是黑色。 性质3:每个叶子节点(NIL)是黑色。 性质4:每个红色结点的两个子结点一定都是黑色。 性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。 https://www.cnblogs.com/skywang12345/p/3245399.html
JDK1.8新特性
Lambda
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。 使用 Lambda 表达式可以使代码变的更加简洁紧凑 是对函数式接口的另外一种重写形式 函数式接口(接口中只有一个抽象方法)
Stream流
foreach : 遍历循环 , 传入Consumer类型对象
filter : 过滤
map : 转化
flatMap : 流的扁平化处理
skip : 跳过N个元素
limit : 截取N个元素
sorted : 排序 , 传入comparator
Collectors : 收集器, 将流中的数据采集成绩和 toList , toMap等
summaryStatistics : 统计方法
count : 统计元素数量
concat : 合并2个流
Optional
Optional类是Java8为了解决null值判断问题,借鉴google guava类库的Optional类而引入的一个同名Optional类,使用Optional类 可以避免显式的null值判断(null的防御性检查),避免null导致的NPE(NullPointerException)
集合
01-常用的集合类有哪些?
Map接口和Collection接口是所有集合框架的父接口: Collection接口的子接口包括:Set接口和List接口 Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及 Properties等 Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等 List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等
02-哪些集合类是线程安全的?
Vector:就比Arraylist多了个 synchronized (线程安全),因为效率较低,现在已经不太建议使 用。 hashTable:就比hashMap多了个synchronized (线程安全),不建议使用。 ConcurrentHashMap:是Java5中支持高并发、高吞吐量的线程安全HashMap实现。
03-Vector如何实现线程安全
Vector 内部提供的各个方法中都会添加synchronized 关键字, 所以是线程安全的
04-HashTable如何实现线程安全 ?
Hashtable是通过使用了 synchronized 关键字来保证其线程安全 , 在其内部提供的方法上都添加了synchronized 关键字
05-遍历一个 List 有哪些不同的方式?
for 循环遍历,基于计数器。在集合外部维护一个计数器,然后依次读取每一个位置的元素, 当读取到最后一个元素后停止。 迭代器遍历,Iterator。Iterator 是面向对象的一个设计模式,目的是屏蔽不同数据集合的特 点,统一遍历集合的接口。Java 在 Collections 中支持了 Iterator 模式。 foreach 循环遍历。foreach 内部也是采用了 Iterator 的方式实现,使用时不需要显式声明 Iterator 或计数器。优点是代码简洁,不易出错;缺点是只能做简单的遍历,不能在遍历过 程中操作数据集合,例如删除、替换。
06-说一下 ArrayList 的优缺点 ?
ArrayList的优点如下: ArrayList 底层以数组实现,是一种随机访问模式。因此查找的时候非常快。 ArrayList 在顺序添加一个元素的时候非常方便。 ArrayList 的缺点如下: 删除元素的时候,需要做一次元素复制操作。如果要复制的元素很多,那么就会比较耗费性 能。 插入元素的时候,也需要做一次元素复制操作
07-如何实现数组和 List 之间的转换?
数组转 List:使用 Arrays. asList(array) 进行转换。 List 转数组:使用 List 自带的 toArray() 方法。 数组转set : 使用构造函数 , 直接传入数组 new HashSet(arrays[])
08-ArrayList 和 LinkedList 的区别是什么?
数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实 现。 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数 据存储方式,所以需要移动指针从前往后依次查找。 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。 内存空间占用:LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储 了两个引用,一个指向前一个元素,一个指向后一个元素。 线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
09-如何把一个线程不安全的集合转化为线程安全集合 ?
通过 Collections 的 synchronizedXxx 方 法将其转换成线程安全的容器后再使用 例如 : Collections.synchronizedList(); Collections.synchronizedSet(list); Collections.synchronizedMap();
10-说一下 HashSet 的实现原理?
HashSet 是基于 HashMap 实现的,HashSet的值存放于HashMap的key上,HashMap的value统 一为present,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。
11-HashSet如何检查重复?HashSet是如何保证数据不可重复的?
向HashSet 中add ()元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合 equles 方法比较。 HashSet 中的add ()方法会使用HashMap 的put()方法。HashMap 的 key 是唯一的在HashMap中如果K/V相同时,会用新的V覆盖掉旧的V,然后返回旧的V。所以不会重复
12-说一下HashMap的实现原理?
HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射 操作,并允许使用null值和null键。 HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。 HashMap 基于 Hash 算法实现的 当我们往HashMap中put元素时,利用key的hashCode重新hash计算出当前对象的元素在数 组中的下标 存储时,如果出现hash值相同的key,此时有两种情况。 如果key相同,则覆盖原始值; 如果key不同(出现冲突),则将当前的key-value放入链表中 获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。
13-HashMap是如何解决hash冲突
核心就是使用了数组的存储方式,然后将冲突的key的对象放入链表中,一旦发现冲突就在链表中做进一步的对比。 需要注意Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转 为红黑树来提高查询效率,数据长度低于6之后会退化为链表
14-HashMap在JDK1.7和1.8有什么区别
resize 扩容优化 , 初始数组长度是16 , 扩容因子默认0.75 , 每次扩容*2 引入了红黑树,目的是避免单条链表过长而影响查询效率 解决了多线程死循环问题,但仍是非线程安全的,多线程时可能会造成数据丢失问题。
15-HashMap的put方法的具体流程?
16-HashMap是如何扩容的 ?
部采用hash表数据加链表的形式,1.8以后引入了红黑树的数据结构 , 初始化数组长度为16 , 当数组长度到0.75时扩容 , 链表长度大于8时转为红黑树 , 红黑树的长度小于6时转为链表结构
17-为什么HashMap的初始长度是16 ?
因为长度太小很容易导致map扩容影响性能,如果分配的太大的话又会浪费资源,所以就使用16作为初始大小 减少hash碰撞 提高map查询效率 分配过小防止频繁扩容 分配过大浪费资源
18- 为什么链表的长度为8是变成红黑树?
应该是一种空间和时间的权衡 , 具体没有了解过
19-HashMap 每次扩容的长度为什么是2的幂次方
为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树 长度大致相同。
20-HashMap 和 ConcurrentHashMap 的区别 ?
ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁 进行保护,相对于HashTable的synchronized锁的粒度更精细了一些,并发性能更好,而 HashMap没有锁机制,不是线程安全的。 HashMap的键值对允许有null,但是ConCurrentHashMap都不允许
21-ConcurrentHashMap 和 Hashtable 的区别?
ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。 底层数据结构: JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现, JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。 Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 数组+链表 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的; 实现线程安全的方式: 在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段 (Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就 不会存在锁竞争,提高并发访问率。(默认分配16个Segment,比Hashtable效率提高16 倍。) 到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑 树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。 Hashtable : 使用 synchronized 来保证线程安全,效率非常低下。
22-什么是TreeMap ?
TreeMap 是一个有序的key-value集合,它是通过红黑树实现的。 TreeMap基于红黑树(Red-Black tree)实现。该映射根据其键的自然顺序进行排序,或者根据 创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
23-comparable 和 comparator的区别?
comparable接口实际上是出自java.lang包,它有一个 compareTo(Object obj)方法用来排序 comparator接口实际上是出自 java.util 包,它有一个compare(Object obj1, Object obj2)方法用 来排序
24-如何实现对集合数据排序
使用Stream中的sorted方法 使用Collections中的sort方法 使用有序的集合 , 例如 : TreeSet , TreeMap
IO
01-java中有几种类型的流?
字节流 : 字节流继承inputStream和OutputStream 字符流 : 字符流继承自Reader和Writer
02-谈谈Java IO里面的常见类,字节流,字符流、接口、实现类、方法阻塞
输入流就是从外部文件输入到内存,输出流主要是从内存输出到文件 字符流中有抽象类InputStream和OutputStream,字节流的子类有 FileInputStream FileOutputStream BufferedOutputStream等。 字符流的子类有 BufferedReader Writer 都实现了Closeable, Flushable, Appendable这些接口。程序中的输入输出都是以流的形式保存的,流中保存的实际上全都是字节文件 java中的阻塞式方法是指在程序调用改方法时,必须等待输入数据可用或者检测到输入结束或者抛出异常,否则程序会一直停留在该语句上,不会执行下面的语句。比如read()和readLine()方法
03-如何将字节流转化为字符流 ?
InputStreamReader 是字节流通向字符流的桥梁 BufferedReader in= new BufferedReader(new InputStreamReader(System.in)); OutputStreamWriter 是字符流通向字节流的桥梁 Writer out = new BufferedWriter(new OutputStreamWriter(System.out))
04-什么是Java序列化,如何实现Java序列化?
序列化就是一种用来处理对象流的机制,将对象的内容进行流化。可以对流化后的对象进行读写操作,可以将流化后的对象传输于网络之间。序列化是为了解决在对象流读写操作时所引发的问题 序列化的实现:将需要被序列化的类实现Serialize接口,没有需要实现的方法,此接口只是为了标注对象可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,再使用ObjectOutputStream对象的write(Object obj)方法就可以将参数obj的对象写出
05-什么是同步和异步 ?
同步: 同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。 异步: 异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。 同步和异步的区别最大在于异步的话调用者不需要等待处理结果,被调用者会通过回调等机制来通知调用者其返回结果。
06-什么是阻塞和非阻塞 ?
阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。 非阻塞: 非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。
07-同步阻塞、同步非阻塞和异步非阻塞又代表什么意思呢?
举个生活中简单的例子,你妈妈让你烧水,小时候你比较笨啊,在哪里傻等着水开(同步阻塞)。等你稍微再长大一点,你知道每次烧水的空隙可以去干点其他事,然后只需要时不时来看看水开了没有(同步非阻塞)。后来,你们家用上了水开了会发出声音的壶,这样你就只需要听到响声后就知道水开了,在这期间你可以随便干自己的事情,你需要去倒水了(异步非阻塞)
08-什么是BIO (Blocking I/O)
同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
09-什么是NIO
NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。
10-什么是AIO (Asynchronous I/O)
AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作
5种IO模型介绍
https://mp.weixin.qq.com/s?__biz=Mzg3MjA4MTExMw==&mid=2247484746&idx=1&sn=c0a7f9129d780786cabfcac0a8aa6bb7&source=41&scene=21#wechat_redirect
多线程
线程基础
01. 线程的状态有几种, 分别是什么 ?
线程的6中状态: New(初始化状态) Runnable(运行状态) Blocked(阻塞状态) Waiting(等待状态) DEAD(死亡状态)
02-创建一个线程的方式有哪些 ?
继承Thread类创建线程类 通过Runnable接口创建线程类 通过Callable和Future创建线程 使用匿名内部类方式创建
03-runnable 和 callable 有什么区别 ?
Callable 接口类似于 Runnable,但是 Runnable 不会返回结果,并且无 法抛出返回结果的异常,而 Callable 功能更强大一些,被线程执行后,可以返回值,这个返回值可以被 Future 拿到,也就是说,Future 可以拿到异步执行任务的返回值。 Future 接口表示异步任务,是一个可能还没有完成的异步任务的结果。所以说 Callable用于产生 结果,Future 用于获取结果。
04-线程的 run()和 start()有什么区别?
run() 调用Thread中定义的方法 start() 启动线程
05-常用的线程同步以及线程调度相关的方法
wait(): 使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁; sleep(): 使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理 InterruptedException 异常; notify(): 唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一 个等待状态的线程,而是由 JVM 确定唤醒哪个线程,而且与优先级无关; notityAll(): 唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它 们竞争,只有获得锁的线程才能进入就绪状态;
06-如何唤醒一个阻塞的线程?
notify(): 唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一 个等待状态的线程,而是由 JVM 确定唤醒哪个线程,而且与优先级无关; notityAll(): 唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它 们竞争,只有获得锁的线程才能进入就绪状态
07-在 Java 程序中怎么保证多线程的运行安全?
使用安全类,比如 java.util.concurrent 下的类,使用原子类AtomicInteger 使用自动锁 synchronized。 使用手动锁 Lock。
08-线程的常用API有哪些
09-线程之间如何通信及线程之间如何同步
通 过 在 线 程 之 间 共 享 对 象 就 可 以 了 , 然 后 通 过 wait/notify/notifyAll 、 await/signal/signalAll 进行唤起和等待
10-说说自己是怎么使用 synchronized 关键字,在项目中用到了吗 ?
synchronized 关键字可以用在如下三个地方 : 修饰实例方法 修饰静态方法 修饰代码块 项目开发过程中很少使用synchronized 关键字修饰 , 因为效率比较低 , 会阻塞, 对于互联网项目来说不太合适 , 一般在单例模式中使用synchronized 实现双重检测锁
11-单例模式了解吗?给我解释一下双重检验锁方式实现单例模式!
懒汉式
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
饿汉式
public class Singleton{ //类加载时就初始化 private static final Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }
双重检查锁
public static Singleton getSingleton() { if (instance == null) { //Single Checked synchronized (Singleton.class) { if (instance == null) { //Double Checked instance = new Singleton(); } } } return instance ; }
12-什么是原子类 , 对Java的JUC框架了解嘛
JUC 是 java.util.concurrent 的简称,在 Java 5.0 提供了 java.util.concurrent(简称JUC)包,这个包包含了一系列能够让 Java 的并发编程变得更加简单轻松的工具类,在这之前,你需要自己手动去实现相关的工具类。JUC包主要包括五部分内容: 1. atomic(原子数据类的包) 2. locks (Java 锁机制---注意区别于synchronized 关键字) 3. collections (提供并发集合框架) 4. executor (线程任务的执行者) 5. tools (辅助工具包 CountDownLatch,CyclicBarrier,Exchanger) 根据修改的数据类型,可以将JUC包中的原子操作类可以分为4类 1 基本类型:AtomicInteger,AtomicLong,AtomicBoolean; 2 数组类型:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray 3 对象的属性修改类型: AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater
13-新建 T1、T2、T3 三个线程,如何保证它们按顺序执行?
用 join 方法
14-什么是线程死锁 ?
死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的 一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了 死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)
15-形成死锁的四个必要条件是什么 ?
互斥条件:在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,就只能等 待,直至占有资源的进程用毕释放。 占有且等待条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进 程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。 不可抢占条件:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过 来。 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。(比如一个进程集合,A在 等B,B在等C,C在等A)
16-如何避免线程死锁 ?
避免一个线程同时获得多个锁 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制
17-线程 B 怎么知道线程 A 修改了变量
volatile 修饰变量 synchronized 修饰修改变量的方法 wait/notify while 轮询探测 , 例如CAS
线程池
01-什么是线程池?使用线程池有哪些好处?
Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用 线程池。在开发过程中,合理地使用线程池能够带来许多好处 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降 低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用
02-创建线程池的参数有哪些
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回 收空闲线程,若无可回收,则新建线程。 newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列 中等待。 newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任 务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
03- 如何创建线程池
使用new的方式 使用Excutors工具类创建
04-线程池的执行流程
提交一个任务到线程池中,线程池的处理流程如下: 判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没 有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个 流程。 线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队 列里。如果工作队列满了,则进入下个流程。 判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任 务。如果已经满了,则交给饱和策略来处理这个任务。
05-如何合理分配线程池大小?
CPU密集型时,任务可以少配置线程数,大概和机器的cpu核数相当,这样可以使得每个线程都在 执行任务 IO密集型时,大部分线程都阻塞,故需要多配置线程数,2*cpu核数
06-线程池启动线程 submit()和 execute()方法有什么不同?
execute 没有返回值,如果不需要知道线程的结果就使用 execute 方法,性能会好很 多。 submit 返回一个 Future 对象,如果想知道线程结果就使用 submit 提交,而且它能 在主线程中通过 Future 的 get 方法捕获线程中的异常。
07-如果你提交任务时,线程池队列已满,这时会发生什么
有俩种可能: 如果使用的是无界队列 LinkedBlockingQueue,也就是无界队列的话,没关系,继续添加任务到 阻塞队列中等待执行,因为 LinkedBlockingQueue 可以近乎认为是一个无穷大的队列,可以无限存放 任务 如果使用的是有界队列比如 ArrayBlockingQueue,任务首先会被添加到ArrayBlockingQueue 中ArrayBlockingQueue 满了,会根据maximumPoolSize 的值增加线程数量,如果增加了线程数量 还是处理不过来,ArrayBlockingQueue 继续满,那么则会使用拒绝策略RejectedExecutionHandler 处理满了的任务,默认是 AbortPolicy
锁
01-多线程中 synchronized 锁升级的原理是什么?
在Java中,锁共有4种状态,级别从低到高依次为:无状态锁,偏向锁,轻量级锁和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级 synchronized 锁升级原理:在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候 threadid 为空,jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,再次进入的时候会先判断 threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为 轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁,此过程就构成了 synchronized 锁的升级。
02-什么是 CAS ?
CAS 是一种基于锁的操作,而且是乐观锁。在 java 中锁分为乐观锁和悲观锁 CAS是通过无限循环来获取数据的,如果在第一轮循环中,a 线程获取地址里面的值被b 线程修改了,那么 a 线程需要自旋到下次循环才有可能机会执行 CAS操作的就是乐观锁,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止 , 可以理解成一个无阻塞多线程争抢资源的模型
03-CAS 的会产生什么问题?
ABA 问题: 从 Java1.5 开始 JDK 的 atomic包里提供了一个类 AtomicStampedReference 来解决 ABA 问题。 循环时间长开销大: 对于资源竞争严重(线程冲突严重)的情况,CAS 自旋的概率会比较大,从而浪费更多的 CPU 资源, 效率低于 synchronized。 只能保证一个共享变量的原子操作: 当对一个共享变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操作,但是对多个共享变量 操作时,循环 CAS 就无法保证操作的原子性,这个时候就可以用锁。
04-什么是偏向锁 ?
偏向锁,顾名思义,它会偏向于第一个访问锁的线程,如果在运行过程中,同步锁只有一个线程访 问,不存在多线程争用的情况,则线程是不需要触发同步的,减少加锁/解锁的一些CAS操作(比 如等待队列的一些CAS操作),这种情况下,就会给线程加一个偏向锁。 如果在运行过程中,遇 到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。
05-什么是轻量级锁 ?
轻量级锁是由偏向所升级来的,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁 争用的时候,轻量级锁就会升级为重量级锁;
06-什么是重量级锁
重量级锁是synchronized ,是 Java 虚拟机中最为基础的锁实现。在这种状态下,Java 虚拟机会阻 塞加锁失败的线程,并且在目标锁被释放的时候,唤醒这些线程。
07-什么是自旋锁 ?
很多 synchronized 里面的代码只是一些很简单的代码,执行时间非常快,此时等待的线程都加锁 可能是一种不太值得的操作,因为线程阻塞涉及到用户态和内核态切换的问题。既然 synchronized 里面的代码执行得非常快,不妨让等待锁的线程不要被阻塞,而是在 synchronized 的边界做忙循环,这就是自旋
08-自旋锁存在什么问题 ?
如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程进入循环等待,消耗CPU。使用不当会造成CPU使用率极高。 上面Java实现的自旋锁不是公平的,即无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题。
09-synchronized 和 Lock 有什么区别?
synchronized是Java内置关键字,在JVM层面,Lock是个Java类; synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。 synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁; 而 lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
10-volatile 关键字的作用
Java 提供了 volatile 关键字来保证可见性和禁止指令重排。 volatile 提供 happensbefore 的保证,确保一个线程的修改能对其他线程是可见的。当一个共享变量被 volatile 修饰 时,它会保证修改的值会立即被更新到主内存中,当有其他线程需要读取时,它会去内存中读取新值。 volatile 可以提供线程间的可见性 , 但是不能保证操作的原子性
ThreadLocal
01-ThreadLocal的底层原理
ThreadLocal是Java中所提供的线程本地存储机制,可以利⽤该机制将数据缓存在某个线程内部, 该线程可以在任意时刻、任意⽅法中获取缓存的数据 ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象中都存在⼀个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的值 如果在线程池中使⽤ThreadLocal会造成内存泄漏,因为当ThreadLocal对象使⽤完之后,应该要 把设置的key,value,也就是Entry对象进⾏回收,但线程池中的线程不会回收,⽽线程对象是通过 强引⽤指向ThreadLocalMap,ThreadLocalMap也是通过强引⽤指向Entry对象,线程不被回收, Entry对象也就不会被回收,从⽽出现内存泄漏,解决办法是,在使⽤了ThreadLocal对象之后,⼿ 动调⽤ThreadLocal的remove⽅法,⼿动清楚Entry对象
02-项目中哪里使用的ThreadLocal
我经历的项目中, 一般用ThreadLocal保存登录用户信息 , 在项目中提供一个过滤器 , 拦截到所有的请求, 获取请求中的UserId头 , 构建User对象保存到ThreadLocal 在ThreadLocal中保存一些当前请求的响应信息 , 在返回的时候还是通过过滤器把响应信息从ThreadLocal中取出来, 返回给客户端 我们锁使用的很多框架内容也使用了ThreadLocal机制 , 例如Spring的事务控制就会将连接放入到ThreadLocal , Seata的分布式事务控制也会将全局事务XID存入到ThreadLocal
03-使用ThreadLocal可能会产生什么问题?如何解决 ?
可能会产生内存泄露问题 : 因为当ThreadLocal对象使⽤完之后,应该要 把设置的key,value,也就是Entry对象进⾏回收,但线程池中的线程不会回收,⽽线程对象是通过 强引⽤指向ThreadLocalMap,ThreadLocalMap也是通过强引⽤指向Entry对象,线程不被回收, Entry对象也就不会被回收,从⽽出现内存泄漏,解决办法是,在使⽤了ThreadLocal对象之后,⼿ 动调⽤ThreadLocal的remove⽅法,⼿动清楚Entry对象
JVM
01-JVM由那些部分组成,运行流程是什么?
流程 : 首先通过编译器把 Java 代码转换成字节码,类加载器(ClassLoader)再把字节码加载到 内存中,将其放在运行时数据区(Runtime data area)的方法区内,而字节码文件只是 JVM 的一 套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎 (Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要 调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。
02-说一下 JVM 运行时数据区
Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存区域划分为若干个不同的数据区域。这 些区域都有各自的用途,以及创建和销毁的时间,有些区域随着虚拟机进程的启动而存在,有些区 域则是依赖线程的启动和结束而建立和销毁。Java 虚拟机所管理的内存被划分为如下几个区域
程序计数器(Program Counter Register) :当前线程所执行的字节码的行号指示器,字节码解 析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳 转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成; 为什么要线程计数器?因为线程是不具备记忆功能 Java 虚拟机栈(Java Virtual Machine Stacks): 每个方法在执行的同时都会在Java 虚拟机栈中创 建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息; 栈帧就是Java虚拟机栈中的下一个单位 本地方法栈(Native Method Stack): 与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的; Native 关键字修饰的方法是看不到的,Native 方法的源码大部分都是 C和C++ 的代码 Java 堆(Java Heap): Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实 例都在这里分配内存; 方法区(Methed Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的 代码等数据。
03-能不能聊一聊你对Java堆的理解 ?
java堆(Java Heap)是java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区 域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例。 在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。 java堆是垃圾收集器管理的主要区域,因此也被成为“GC堆” 从内存回收角度来看java堆可分为:新生代和老年代。 从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区。 无论怎么划分,都与存放内容无关,无论哪个区域,存储的都是对象实例,进一步的划分都是为了 更好的回收内存,或者更快的分配内存。
04-能不能聊一聊对Java方法区的理解
方法区是所有线程共享的内存区域,它用于存储已被Java虚拟机加载的类信息、常量、静态变量、 即时编译器编译后的代码等数据。 它有个别命叫Non-Heap(非堆)。当方法区无法满足内存分配需求时,抛出OutOfMemoryError 异常。
05-知道直接内存吗?
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机中定义的内 存区域。但是这部分内存也被频繁地使用,而且也可能导致 OutOfMemoryError 异常出现,所以 我们放到这里一起讲解。 我的理解就是直接内存是基于物理内存和Java虚拟机内存的中间内存
06-堆栈的区别是什么?
注意: 静态变量放在方法区 , 静态的对象还是放在堆。
07-知道深拷贝和浅拷贝嘛 ?
浅拷贝/浅复制(shallowCopy)只是增加了一个指针指向已存在的内存地址 User u1 = new User(); User u2 = u1 ; 深拷贝/深复制(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新 的内存 重写clone方法 通过JSON转换
08-强引用、软引用、弱引用、虚引用的区别?
强引用 我们平时new了一个对象就是强引用,例如 Object obj = new Object(); 即使在内存不足的情况下,JVM 宁愿抛出OutOfMemory错误也不会回收这种对象 软引用 如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会 回收这些对象的内存。 SoftReference softRef = new SoftReference(str); // 软引用 弱引用 具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦 发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存 String str=new String("abc"); WeakReference abcWeakRef = new WeakReference(str); 虚引用 如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚 引用主要用来跟踪对象被垃圾回收器回收的活动。
09-有没有了解过GC ?
GC 是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java 提供的 GC 功能可以自动监测对象 是否超过作用域从而达到自动回收内存的目的,Java 语言没有提供释放已分配内存的显示操作方法。 在JVM中, 有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前 堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合 中,进行回收。
10-垃圾回收器的原理是什么?有什么办法手动进行垃圾回收?
对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。 通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是"可达的",哪些对象是"不可达的"。当GC确定一些对象为"不可达"时,GC就有责任回收这些内存空 间。 程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执 行 。
11-知不知道FullGC ?
清理整个堆空间—包括年轻代和老年代和永久代 因为Full GC是清理整个堆空间所以Full GC执行速度非常慢,在Java开发中最好保证少触发Full GC
12-讲一下新生代、老年代、永久代的区别
在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。而新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。这样划分的目的是为了使 JVM 能够 更好的管理堆内存中的对象,包括内存的分配以及回收。 新生代中一般保存新出现的对象,所以每次垃圾收集时都发现大批对象死去,只有少量对象存活, 便采用了 复制算法 ,只需要付出少量存活对象的复制成本就可以完成收集。 老年代中一般保存存活了很久的对象,他们存活率高、没有额外空间对它进行分配担保,就必须采 用 “标记-清理”或者“标记-整理” 算法。 永久代就是JVM的方法区。在这里都是放着一些被虚拟机加载的类信息,静态变量,常量等数据。 这个区中的东西比老年代和新生代更不容易回收。
13-Minor GC、Major GC、Full GC是什么 ?
Minor GC是新生代GC,指的是发生在新生代的垃圾收集动作。由于java对象大都是朝生夕死的, 所以Minor GC非常频繁,一般回收速度也比较快。(一般采用复制算法回收垃圾) Major GC是老年代GC,指的是发生在老年代的GC,通常执行Major GC会连着Minor GC一起执 行。Major GC的速度要比Minor GC慢的多。(可采用标记清楚法和标记整理法) Full GC是清理整个堆空间,包括年轻代和老年代
14-对象什么时候可以被垃圾器回收 ?
当对象对当前使用这个对象的应用程序变得不可触及的时候,这个对象就可以被回收了。 垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。
15-JVM 垃圾回收算法有哪些?
标记-清除算法: 标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。 复制算法: 按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块 上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。 标记-整理算法: 标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的 内存。 分代算法: 根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用 复制算法,老年代采用标记整理算法。
16-JVM中的永久代中会发生垃圾回收吗?
垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。 垃圾收集器以及新生代、老年代、永久代
17-什么是类加载器,类加载器有哪些?
主要有以下四种类加载器: 启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用。 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提 供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。 用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现
18-说一下类装载的执行过程?
类装载分为以下 5 个步骤: 加载:根据查找路径找到相应的 class 文件然后导入; 验证:检查加载的 class 文件的正确性; 准备:给类中的静态变量分配内存空间; 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。 初始化:对静态变量和静态代码块执行初始化工作。
19-知道什么事双亲委派嘛 ?
双亲委派模型:如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这 个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到 顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时, 子加载器才会尝试去加载类。 总 结就是: 当一个类收到了类加载请求时,不会自己先去加载这个类,而是将其委派给父类,由父类去加 载,如果此时父类不能加载,反馈给子类,由子类去完成类的加载。
20-如何打破双亲委派 ?
自定义类继承ClassLoader类,重写loadClass和findClass方法
21-有接触过JVM调优嘛 ?
JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这 两款视图监控工具。 jconsole:用于对 JVM 中的内存、线程和类等进行监控; jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。
22-常用的JVM调优参数有哪些 ?
#常用的设置 -Xms:初始堆大小,JVM 启动的时候,给定堆空间大小。 -Xmx:最大堆大小,JVM 运行过程中,如果初始堆空间不足的时候,最大可以扩展到多少。 -Xmn:设置堆中年轻代大小。整个堆大小=年轻代大小+年老代大小+持久代大小。 -XX:NewSize=n 设置年轻代初始化大小大小 -XX:MaxNewSize=n 设置年轻代最大值 -XX:NewRation 设置年轻代和年老代的比值。如: -XX:NewRatio=3,表示年轻代与年老代比值为 1:3,年轻代占整个年轻代+年老代和的 1/4 -XX:SurvivorRatio=n 年轻代中 Eden 区与两个 Survivor 区的比值。注意 Survivor 区有两个。8表示两个Survivor :eden=2:8 , 即一个Survivor占年轻代的1/10,默认就为8 -Xss:设置每个线程的堆栈大小。JDK5后每个线程 Java 栈大小为 1M,以前每个线程堆栈大小为 256K。 -XX:ThreadStackSize=n 线程堆栈大小 -XX:PermSize=n 设置持久代初始值 -XX:MaxPermSize=n 设置持久代大小 -XX:MaxTenuringThreshold=n 设置年轻带垃圾对象最大年龄。如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代。
23-JVM 调优的参数可以在那设置参数值
如果上线了是WAR包的话可以在Tomcat设置 如果是Jar包直接 :java -jar 是直接插入JVM命令就好了 java -Xms1024m -Xmx1024m ...等等等 JVM参数 -jar springboot_app.jar &
JAVAWEB
Servlet
01-什么是servlet?
servlet是用来处理客户端请求并产生动态网页内容的java类
02-Tomcat的缺省端口是多少,怎么修改?
默认端口号是8080 如果是外置的Tomcat, 在config/server.xml中修改
03-Servlet生命周期知道嘛 ?
加载Servlet 初始化 请求处理 销毁
04- 知道转发和重定向嘛 ?
1.实现方式不一样 : 转发: requset.getRequsetDispatcher(“/***.jsp”).forward(request,response) 重定向 : response.sendRedirect(“/***.jsp”) 2. forward是服务器端发起的请求;redirect是客户端发起的请求; 3. forward只有一次请求;redirect会产生2次请求。 4. forward数据会同时转发过去;redirect数据不会同时传递过去。
05- 什么是cookie?什么是session?
Cookie 是客户端保存用户信息的一种机制,用来记录用户的一些信息,它的过期时间可以任意设置,如果不主动清除,很长一段时间都能保留 Session 是在无状态的HTTP协议下,服务端记录用户状态时用于标识具体用户的机制,它是在服务端保存的用来跟踪用户的状态的数据结构,可以保存在文件、数据库、或者集群中。
06-Cookie和Session有什么区别 ?
Session: 数据存放在服务端,安全(只存放和状态相关的) session不仅仅是存放字符串,还可以存放对象 session是域对象(session本身是不能跨域的,但可以通过相应技术来解决) sessionID的传输默认是需要cookie支持的 Cookie 数据存放在客户端,不安全(存放的数据一定是和安全无关紧要的数据) cookie只能存放字符串,不能存放对象 cookie不是域对象(Cookie是支持跨域的)
07- Tomcat是如何创建servlet类实例?用到了什么原理
当tomcat启动时,会读取在webapps目录下所有的web应用中的web.xml文件。然后对xml文件进行解析,并读取servlet注册信息。然后将每个应用中注册的servlet类都进行加载,并通过反射的方式实例化
08-Servlet是单例的还是多例的 ?
servlet是单列模式创建的,只实例化一次,同一个servlet可以处理多个用户请求,当同时有两个用户访问时,则会启动两个负责处理请求的servlet线程,所以会出现线程安全问题
09-说一下过滤器,拦截器,监听器的区别 ?
过滤器 , 监听器以及Servlet是JAVAWEB的核心三个组件 监听器用户监听整个应用程序 , 例如 ServletContext容器创建销毁 , Session创建销毁 ,Request创建销毁等 过滤器用于过滤用户请求 , 过滤资源路径可以在web.xml中配置, 也可以使用@WebFilter注解配置 Servlet用于处理用户请求 拦截器是SpringMVC中的技术, 对请求进行拦截 , 拦截的是进入到SpringMVC框架内部的请求 , Filter可以拦截所有的web请求
MYSQL
基础语法
DDL : 数据定义语言 , 例如 : create ,drop等控制数据库表结构的
DCL : 数据库控制语言 , GRANT和REVOKE 等对数据库进行控制的
DML : 数据管理语言 ,常用的就是INSERT、UPDATE、DELETE等对数据库的数据进行一些操作
DQL : 数据查询语言 ,数据检索语句,用于从表中获取数据。通常最常用的为保留字SELECT,并且常与FROM子句、WHERE子句组成查询SQL查询语句
基础的语法顺序 : Select [字段,...] From 表名 [Where 条件] [group by 字段] [having 条件][Order By 字段 顺序 ]
SQL排序 : Order By字段 顺序
SQL分组 : Group By 分组字段
SQL去重
Distinct
Group by
SQL分页 : Limit [start ] size
SQL函数
当前时间 : now()/sysdate
字符串拼接 : concat
截取空格 : TRIM
替换字符串 : REPLACE
字符串截取 : SUBSTRING
字符串反转 : REVERSE
获取当前年 :Year
获取当前月 :Month
获取当前周 : week
时间计算 : DATE_ADD
条件判断 : IF(expr,v1,v2) / IFNULL(v1,v2)
多表查询
内连接
隐式内连接 : select * from A,B where 连接条件
显式内连接 : select * from A inner join B on连接条件
外连接
左外连接
select * from A left join B on连接条件
右外连接
select * from A right join B on连接条件
存储引擎
优化
设计
选择合适的存储引擎 , 合适的字段类型 , 遵循范式(反范式设计)
存储引擎 : 不需要事务, 不需要外键读写较多的的使用MyIsam 需要事务, 需要外键的使用InnoDB 合适的字段类型 : 定长字符串用char , 不定长用varchr 状态, 性别等有限数量值的用tinyint 遵循范式 : 第一范式1NF,原子性 第二范式2NF,消除部分依赖 第三范式3NF,消除传递依赖 在互联网项目中为了减少多表查询提高效率, 可以做一些冗余设计
功能:索引,缓存,分库分表。
架构:主从复制,读写分离,负载均衡。mycat / shardingjdbc
合理SQL: 测试,经验
MYSQL超大分页怎么处理 ?
MYSQL 不是跳过offset行, 而是取offset+N行, 然后放弃前offset行 , 返回N行, 所以当offset比较法的情况下分页效率很低 正确的处理方法是 : 先快速定位需要获取的id再关联查询获取数据
索引
01-知道MYSQL索引嘛 ?
索引是帮助MySQL高效获取数据的数据结构。更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度 一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往是存储在磁盘上的文件中的
02-你所知道的Mysql有哪些索引 ?
普通索引 index:对索引字段没有要求。 唯一索引 unique index:要求索引字段值不能重复。同时增加唯一约束。 主键索引 primary key:要求索引字段值不能重复,也不能为NULL。同时增加主键约束。 复合索引 全文索引 fulltext key:索引字段的来源不是所有字段的数据,而是从字段中提取的特别关键词。
03-索引的优势和劣势 知道嘛 ?
优势: 可以提高数据检索的效率,降低数据库的IO成本,类似于书的⽬目录。 通过索引列对数据进行行排序,降低数据排序的成本,降低了了CPU的消耗 劣势: 索引会占据磁盘空间 索引虽然会提高查询效率,但是会降低更新表的效率。比如每次对表进行增删改操作,MySQL不仅要保存数据,还要保存或者更新对应的索引文件
04-索引是怎么存储的知道嘛 ?
索引是在存储引擎中实现 的,也就是说不同的存储引擎,会使用不同的索引 MyISAM和InnoDB存储引擎: 只⽀支持B+ TREE索引, 也就是说默认使用BTREE,不能够更换 MEMORY/HEAP存储引擎: 支持HASH和BTREE索引
05-B树和B+树有什么区别 ?
B树和B+树的最大区别在于非叶子节点是否存储数据 。 B树是非叶子节点和叶子节点都会存储数据。 B+树只有叶子节点才会存储数据,而且存储的数据都是在⼀行上,而且这些数据都是有指针指向的,也就是有顺序的。
06-知道什么是非聚集索引 (非聚簇索引)嘛 ?
在使用MyISAM存储引擎的时候, B+树叶子节点只会存储数据行的指针,简单来说数据和索引不在一起 , 这种索引称之为非聚集索引
主键索引
普通索引
在 MyISAM 中,主键索引和辅助索引在结构上没有任何区别, 只是主键索引要求 key 是唯一的, 而辅助索引的 key 可以重复
07-知道什么是聚集索引(聚簇索引)嘛 ?
聚集索引实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键 , 然后用主键到主索引中检索获得记录
主键索引
在使用InnoDB存储引擎的时候, B+树叶子节点会存储数据行记录,简单来说数据和索引在一起存储 InnoDB 要求表必须有主键 , 如果没有显式指定,则 MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL 自动为 InnoDB 表生成一个隐含字段作为主键, 类型为长整形。
辅助索引
与 MyISAM 索引的不同是 InnoDB 的辅助索引 data 域存储相应记录主键的值而不是地址。换句话说 , InnoDB 的所有辅助索引都引用主键索引的data 域
08-在一个非主键字段上创建了索引, 想要根据该字段查询到数据, 需要查询几次 ?
MyIsam :先从索引树中查询数据, 根据数据物理地址, 获取数据 InnoDB : 先查找辅助索引 , 再去组件索引查找数据
09-使用UUID作为数据库的主键, 是否合适 ?
不合适, UUID比较长会导致索引占用更多的存储空间
10-知道什么是回表查询嘛 ?
在InnoDB中查询一个非主键索引字段, 会先查找辅助索引找到数据的Id值, 再根据id值查询主键索引 , 定位行记录 , 这个过程就叫会表查询 , 会表查询的效率比扫描一遍索引树低
11-知道什么是覆盖索引嘛 ?
覆盖索引是指只需要在一棵索引树上就能获取SQL所需的所有列数据 , 因为无需回表查询效率更高 实现覆盖索引的常见方法是:将被查询的字段,建立到联合索引里去
12-知道什么是左前缀原则嘛 ?
在mysql建立联合索引时会遵循左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配,组合索引的第一个字段必须出现在查询组句中,这个索引才会被用到 例如 :为user表创建了一个联合索引 (age,name,sex);实际相当于建立了(age) , (age,name) , (age,name,sex) 三个索引 在执行查询的时候MYSQL会对SQL顺序进行调整尽量匹配索引
13-什么情况下索引会失效 ?
1. 范围查询 mysql 会一直从左向右匹配直到遇到范围查询( >、 )就停止匹配。范围列可以用到索引,但是范围列后面的列无法用到索引。 2. like 语句 如果通配符 % 不出现在开头,则可以用到索引, like “value%”可以使用索引,但是 like “%value%” 不会使用索引,走的是全表扫描 3.列上使用函数或表达式 如果查询条件中含有函数或表达式,将导致索引失效而进行全表扫描 4.根据null值查询 只要列中包含有 NULL 值都将不会被包含在索引中,复合索引中只要有一列含有 NULL 值,那么这一列对于此复合索引就是无效的。所以在数据库设计时不要让字段的默认值为 NULL
14-如果一条SQL语句执行很慢 , 如何找到慢的原因 ?
开启慢查询日志或者使用监控工具监控慢查询 , 例如 : skywalking 为常用的查询字段或者条件建立索引 使用Explain关键字查看SQL执行计划 , 看索引是否命中 , 以及命中的级别 根据情况修改SQL语句
15-Explain查看SQL执行计划有哪些常用参数 ?
id
每个 SELECT语句都会⾃动分配的⼀个唯⼀标识符. 表示查询中操作表的顺序,有三种情况: id相同:执⾏顺序由上到下 id不同:如果是⼦查询,id号会⾃增,id越⼤,优先级越⾼。 id相同和不同同时存在 id列为null的就表示这是⼀个结果集,不需要使⽤它来进⾏查询。
select_type
查询类型,主要⽤于区别普通查询、联合查询(union、union all)、⼦查询等复杂查询。
table
显示的查询表名, 如果查询使⽤了别名,那么这⾥显示的是别名 如果不涉及对数据表的操作,那么这显示为null 如果显示为尖括号括起来的就表示这个是临时表,后边的N就是执⾏计划中的id,表示结果来⾃于 这个查询产⽣ 如果是尖括号括起来的,与 类似,也是⼀个临时表,表示这个结果来⾃于union查询 的id为M,N的结果集。
type
索引的使用类型 , 性能由好到差依次为 : system,const,eq_ref,ref,fulltext,ref_or_null,unique_subquery,index_subquery,range,index_merge,index,ALL 除了all之外,其他的type都可以使⽤到索引,除了index_merge之外,其他的type只可以⽤到⼀个索引
key
查询真正使⽤到的索引,select_type为index_merge时,这⾥可能出现两个以上的索引,其他的 select_type这⾥只会出现⼀个。
Extra
包含不适合在其他列中显示单⼗分重要的额外的信息,这个列可以显示的信息⾮常多,有⼏⼗ 种
...
16-你们使用索引是越多越好嘛 ?
不是, 要根据具体的情况来决定是否建索引 需要创建索引情况 主键自动建立主键索引 频繁作为查询条件的字段应该创建索引 多表关联查询中,关联字段应该创建索引 (on 两边都要创建索引) 查询中排序的字段,应该创建索引 频繁查找字段 , 应该创建索引 查询中统计或者分组字段,应该创建索引 不要创建索引情况 表记录太少 经常进⾏行行增删改操作的表 频繁更新的字段 where条件里使用频率不高的字段 多使用组合索引 在多个列上建立索引 , 这种索引叫做复合索引(组合索引) , 复合索引在数据库操作期间所需的开销更小, 可以代替多个单一索引 而且更省空间, 而且更容易实现覆盖索引
锁
01-什么是锁?MySQL 中提供了几类锁?
锁是实现数据库并发控制的重要手段,可以保证数据库在多人同时操作时能够正常运行。MySQL 提供了全局锁、行级锁、表级锁。其中 InnoDB 支持表级锁和行级锁,MyISAM 只支持表级锁。
02-什么是死锁 , 什么情况下会产生死锁 ?
所谓死锁(DeadLock)是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。MYSQL中表级锁不会产生死锁 , 只有行级锁才会产生死锁 MYSQL出现死锁后会直接结束会话 ,报Deadlock错
03-如何处理死锁 ?
对待死锁常见的两种策略: 通过 innodblockwait_timeout 来设置超时时间,一直等待直到超时; 发起死锁检测,发现死锁之后,主动回滚死锁中的某一个事务,让其它事务继续执行。
04-如何查看死锁 ?
使用命令 show engine innodb status 查看最近的一次死锁。 InnoDB Lock Monitor 打开锁监控,每 15s 输出一次日志。使用完毕后建议关闭,否则会影响数据库性能。
05-如何避免死锁?
可以在事务开始时通过SELECT ... FOR UPDATE 语句来获取意向锁 在事务中,如果要更新记录,应该直接申请排他锁,而不应先申请共享锁、更新时再申请排他锁 如果事务需要修改或锁定多个表,则应在每个事务中以相同的顺序使用加锁语句。 通过 SELECT ... LOCK IN SHARE MODE 获取行的读锁后,如果当前事务再需要对该记录进行更新操作,则很有可能造成死锁。 改变事务隔离级别。
06-InnoDB 默认是如何对待死锁的?
InnoDB 默认是使用设置死锁时间来让死锁超时的策略,默认 innodblockwait_timeout 设置的时长是 50s。
07-什么是全局锁?它的应用场景有哪些?
全局锁就是对整个数据库实例加锁,它的典型使用场景就是做全库逻辑备份。 这个命令可以使整个库处于只读状态。使用该命令之后,数据更新语句、数据定义语句、更新类事务的提交语句等操作都会被阻塞。
08-什么是共享锁?
共享锁又称读锁 (read lock),是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。当如果事务对读锁进行修改操作,很可能会造成死锁。
09-什么是排它锁?
排他锁 exclusive lock(也叫 writer lock)又称写锁。 若某个事物对某一行加上了排他锁,只能这个事务对其进行读写,在此事务结束之前,其他事务不能对其进行加任何锁,其他进程可以读取,不能进行写操作,需等待其释放
10-InnoDB 存储引擎有几种锁算法?
Record Lock — 单个行记录上的锁; Gap Lock — 间隙锁,锁定一个范围,不包括记录本身; Next-Key Lock — 锁定一个范围,包括记录本身。
11-你在开发过程中有对MYSQL加锁嘛 ?
没有
事务
01-事务的ACID知道嘛 ?
原子性(Atomicity): 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 一致性(Consistency): 事务前后数据的完整性必须保持一致。 隔离性(Isolation): 事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。 持久性(Durability): 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
02-事务有哪几种隔离级别呢?可以详细谈谈吗?
读未提交(read uncommitted) 读以提交(read committed)RC 可重复读(repeatable read)RR 串行(serializable)
03-不考虑事务的隔离级别会有什么问题
并发事务可能造成:脏读、不可重复读和幻读等问题 ,这些问题其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决
04-MYSQL的默认隔离级别是什么知道嘛 , 如何调整隔离级别 ?
MYSQL的默认隔离级别是可重复读(RepeatableRead) Oracle的默认隔离级别是读已提交(ReadCommited) 调整隔离级别的方式 : 1.查看隔离级别: 通过show variables like '%tx_isolation%'查看当前事务的隔离级别 2.设置会话隔离级别: set session transaction isolation level 隔离级别 3. 设置全局隔离级别 通过修改配置文件 /etc/my.cnf , 在文件的文末添加配置:transaction-isolation =隔离级别来设置 4.程序中一般通过Spring控制事务, 可以在事务属性上,通过isolation 属性设置隔离级别
05-什么是MVCC?
MVCC 全称是多版本并发控制系统,InnoDB 和 Falcon 存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决幻读问题
06-MVCC原理知道嘛 ?
InnoDB 的 MVCC 是通过在每行记录后面保存两个隐藏的列来实现,这两个列一个保存了行的创建时间,一个保存行的过期时间(删除时间)。当然存储的并不是真实的时间而是系统版本号(system version number)。 每开始一个新的事务,系统版本号都会自动新增,事务开始时刻的系统版本号会作为事务的版本号,用来查询到每行记录的版本号进行比较。
07-MySQL 事务实现原理是什么?
事务的实现是基于数据库的存储引擎,不同的存储引擎对事务的支持程度不一样。MySQL 中支持事务的存储引擎有InnoDB 和 NDB。 InnoDB 是高版本 MySQL 的默认的存储引擎,InnoDB 是通过多版本并发控制(MVCC,Multiversion Concurrency Control )解决不可重复读问题,加上间隙锁(也就是并发控制)解决幻读问题。 事务的隔离性是通过锁实现,而事务的原子性、一致性和持久性则是通过事务日志实现。
SSM框架
Spring
01-什么是Spring框架?
Spring是一种轻量级框架,旨在提高开发人员的开发效率以及系统的可维护性。 我们一般说的Spring框架就是Spring Framework,它是很多模块的集合, 例如 :数据访问/集成、Web、AOP(面向切面编程)、工具、消息和测试模块。比如Core Container中的Core组件是Spring所有组件的核心,Beans组件和Context组件是实现IOC和DI的基础,AOP组件用来实现面向切面编程
02-谈谈自己对于Spring IOC的认识
IOC(Inversion Of Controll,控制反转) 是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由给Spring框架来管理。IOC容器是Spring用来实现IOC的载体,IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象 将对象之间的相互依赖关系交给IOC容器来管理,并由IOC容器完成对象的注入就是依赖注入
03-谈谈自己对于AOP的理解
AOP(Aspect-Oriented Programming,面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性 , Spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK动态代理去创建代理对象;而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用CGlib动态代理生成一个被代理对象的子类来作为代理
04-Spring AOP和AspectJ AOP有什么区别?
Spring AOP是属于运行时增强,而AspectJ是编译时增强。 Spring AOP基于代理(Proxying),而AspectJ基于字节码操作(Bytecode Manipulation)。 Spring AOP已经集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。 AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单。 如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择AspectJ,它比SpringAOP快很多
05-Spring中的单例bean的线程安全问题了解吗?
大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例bean存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。 有两种常见的解决方案: 1.在bean对象中尽量避免定义可变的成员变量(不太现实)。 2.在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal中(推荐的一种方式)
06-Spring中的bean生命周期?
1.Bean容器找到配置文件中Spring Bean的定义。 2.Bean容器利用Java Reflection API创建一个Bean的实例。 3.如果涉及到一些属性值,利用set()方法设置一些属性值。 4.如果Bean实现了BeanNameAware接口,调用setBeanName()方法,传入Bean的名字。 5.如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。 6.如果Bean实现了BeanFactoryAware接口,调用setBeanClassFacotory()方法,传入ClassLoader对象的实例。 7.与上面的类似,如果实现了其他*Aware接口,就调用相应的方法。 8.如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessBeforeInitialization()方法。 9.如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法。 10.如果Bean在配置文件中的定义包含init-method属性,执行指定的方法。 11.如果有和加载这个Bean的Spring容器相关的BeanPostProcess对象,执行postProcessAfterInitialization()方法。 12.当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destroy()方法。 13.当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法。
07-Spring 框架中都用到了哪些设计模式?
工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例; 单例模式:Bean默认为单例模式。 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术; 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的 对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。
08-你用到Spring中的哪些注解 ?
@Controller : 作用在控制器上
@Service : 作用在业务层类上
@Repository : 作用在数据访问层类上
@Componet : 作用在类上
@Bean : 作用在方法上声明Bean对象
@Scope : 声明@Bean的作用范围 (单例/多例)
@Autowired : 按类型注入Bean
@Resource : 按名称注入Bean
@Qualifier : 多个类型相同情况下, 指定按名称注入的名称
@PropertiesSource : 加载配置文件
@Import : 导出其他的配置类
@Configration : 标注在配置类上
@ConfigrationProperties : 批量加载配置文件配置
@Transational : 事务支撑
@EnableGlobalTransational : 开启事务支撑
@EnableAspectJProxy : 开启代理支持
@Aspect : 声明切面
@Before : 标注前置通知
@After : 标注最终通知
@Around : 标注环绕通知
@AfterThrowing : 标注异常通知
@AfterReturing : 标注后置通知
@PointCut : 标注方法, 声明切入点表达式
09-Spring事务管理的方式有几种?
1.编程式事务: 在代码中硬编码(不推荐使用)。 2.声明式事务: 在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声明式事务。
10-Spring事务中的隔离级别有哪几种?
在TransactionDefinition接口中定义了五个表示隔离级别的常量: ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ隔离级别;Oracle默认采用的READ_COMMITTED隔离级别。 ISOLATION_READ_UNCOMMITTED:读未提交 ISOLATION_READ_COMMITTED:读已提交 ISOLATION_REPEATABLE_READ:可重复读 ISOLATION_SERIALIZABLE:串行化
11-Spring中什么时候 @Transactional会失效
因为Spring事务是基于代理来实现的,所以某个加了@Transactional的⽅法只有是被代理对象调⽤时, 那么这个注解才会⽣效,所以如果是被代理对象来调⽤这个⽅法,那么@Transactional是不会失效的。 同时如果某个⽅法是private的,那么@Transactional也会失效,因为底层cglib是基于⽗⼦类来实现 的,⼦类是不能重载⽗类的private⽅法的,所以⽆法很好的利⽤代理,也会导致@Transactianal失效
12-说一下Spring的事务传播行为 ?
可以在事务属性中通过propagation属性来指定事务传播行为 : PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就 加入该事务,该设置是最常用的设置。 PROPAGATION_SUPPORTS(默认):支持当前事务,如果当前存在事务,就加入该事务,如果当前不 存在事务,就以非事务执行。 PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前 不存在事务,就抛出异常。 PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。 PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前 事务挂起。 PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则 按REQUIRED属性执行。
13-Spring中的事务是如何实现的 ?
Spring事务底层是基于数据库事务和AOP机制的 ⾸先对于使⽤了@Transactional注解的Bean,Spring会创建⼀个代理对象作为Bean 当调⽤代理对象的⽅法时,会先判断该⽅法上是否加了@Transactional注解 如果加了,那么则利⽤事务管理器创建⼀个数据库连接 并且修改数据库连接的autocommit属性为false,禁⽌此连接的⾃动提交,这是实现Spring事务⾮ 常重要的⼀步 然后执⾏当前⽅法,⽅法中会执⾏sql 执⾏完当前⽅法后,如果没有出现异常就直接提交事务 如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务 Spring事务的隔离级别对应的就是数据库的隔离级别 Spring事务的传播机制是Spring事务⾃⼰实现的,也是Spring事务中最复杂的 Spring事务的传播机制是基于数据库连接来做的,⼀个数据库连接⼀个事务,如果传播机制配置为需要新开⼀个事务,那么实际上就是先建⽴⼀个数据库连接,在此新数据库连接上执⾏sql
14-使用@Autowired注解自动装配的过程是怎样的?
使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文 件进行配置 在启动spring IOC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理 器,当容器扫描到@Autowied、@Resource或 @Inject时,就会在IOC容器自动查找需要的bean, 并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean: 如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据; 如果查询的结果不止一个,那么@Autowired会根据名称来查找; 如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false
15-spring 自动装配 bean 有哪些方 式?
在Spring框架xml配置中共有5种自动装配: no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相 同,就进行自动装配。 byType:通过参数的数据类型进行自动装配。 constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。 autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。
16-Spring支持的几种bean的作用域 Scope ?
Spring框架支持以下五种bean的作用域: singleton prototype request session global-session
SpringMVC
01-什么是Spring MVC ?简单介绍下你对springMVC的理解?
Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
02-描述一下SpringMVC的工作流程
用户发送请求至前端控制器DispatcherServlet; DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle; 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生 成)一并返回给DispatcherServlet; DispatcherServlet 调用 HandlerAdapter处理器适配器; HandlerAdapter 经过适配调用具体处理器(Handler,也叫后端控制器); Handler执行完成返回ModelAndView; HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet; DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析; ViewResolver解析后返回具体View; DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中) DispatcherServlet响应用户。
03-Spring MVC常用的注解有哪些?
@RestController : @Controller和@ResponseBody的组合注解
@RequestMapping : 接口请求映射
@GetMapping : GET请求方式映射
@PutMapping : PUT请求方式映射
@PostMapping : PUT请求方式映射
@DeleteMapping : DELETE请求方式映射
@RequestBody : 接收请求体数据,JSON会自动转对象
@ResponseBody : 响应数据自动转JSON
@RequestParam : 接收请求参数
@Cookie : 接收请求中的cookie
@RequestHeader : 接收请求头
@PathVariable : 声明路径变量
04-在浏览器输入地址到服务器响应数据 , 整个过程知道嘛 ?
1、在浏览器中输入url,直接输入ip或者输入域名 2、如果输入的是域名就需要通过DNS解析将域名解析成IP地址,通过IP来确认访问的是哪个服务器 3、建立TCP请求(即三次握手) 4、发送http请求 5、服务器处理请求,并将结果返回给浏览器 6、最后断开TCP连接(即四次挥手) 7、浏览器根据返回结果进行处理以及页面渲染
Mybatis
01-有没有了解过Mybatis的工作原理 ?
读取 MyBatis 配置文件 加载映射文件Mapper.xml 构造会话工厂SqlSessionFactory 创建会话对象SqlSession 通过Executor 执行器动态地生成需要执行的 SQL 语句 MappedStatement 对象存储要映射的 SQL 语句的 id、参数等信息。 执行输入参数映射 根据查询结果执行输出结果映射封装数据
02-#{}和${}有什么区别 ?
#{}是占位符,预编译处理;${}是拼接符,字符串替换,没有预编译处理。 Mybatis在处理#{}时,#{}传入参数是以字符串传入,会将SQL中的#{}替换为?号,调用 PreparedStatement的set方法来赋值。 #{} 可以有效的防止SQL注入,提高系统安全性;${} 不能防止SQL 注入 #{} 的变量替换是在数据库系统中; ${} 的变量替换是在 数据库系统外
03-在mapper中如何传递多个参数 ?
顺序传参法 ,arg0代表第一个参数 arg1代表第二个参数 使用@Param注解传参法 封装到对象或者Map集合
04-mybatis映射文件中用到哪些标签
select : 查询标签
update : 更新标签
delete : 删除标签
insert : 插入标签
where : 类似于SQL语句的where
if : 动态SQL对条件进行判断
foreach : 遍历循环 , 拼接SQL , 一般用于批量处理
set : 类似于SQL语句中 的set关键词
resultMap : 结果集映射标签
05-mybatis如何实现多表查询 ?
编写多表查询SQL语句 , 使用ResultMap建立结果集映射 使用延迟加载 , 多个SQL语句组合 主要使用的注解就是 : collection : 建立一对多映射 association : 简历一对一映射
06-foreach标签哪些属性
collection : 指定遍历的集合, 如果遍历的是数组就是array , 遍历的是list集合就是list , 遍历的是对象或者Map的集合属性就是对象属性名称或者map的key
item : 遍历的每一项
index : 遍历过程中的索引
open : 字符串拼接前缀
separator : 分隔符
close : : 字符串拼接后
07-Mybatis如何获取生成的主键
insert 标签上使用useGeneratedKeys="true" keyProperty 在insert标签内部使用selectKey标签配置keyProperties , keyColum通过select last_insert_id()查询
08-当实体类中的属性名和表中的字段名不一样 ,怎么办
第1种: 通过在查询的SQL语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。 第2种: 通过 来映射字段名和实体类属性名的一一对应的关系。
09-使用MyBatis的mapper接口调用时有哪些要求?
Mapper接口方法名和mapper.xml中定义的每个sql的id相同。 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相 同。 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。 Mapper.xml文件中的namespace即是mapper接口的类路径。
10-Dao接口里的方法,参数不同 时,方法能重载吗 ?
Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。
11-Mybatis是如何进行分页的?分页插件的原理是什么?
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分 页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。 分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截 待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。 举例:select * from student,拦截sql后重写为:select t.* from (select * from student) t limit 0, 10
12-Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是 一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。 它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调 a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完 成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
13-Mybatis的一级、二级缓存 ?
一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存 二级缓存与一级缓存其机制相同 ,默认也是采用 HashMap 存储,不同在于其 存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓 存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状 态),可在它的映射文件中配置 对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear
Mybatis-Plus
01-使用Mybatis-Plus的过程中多表查询如何做 ?
使用mybtis的映射配置文件, 自己编写SQL语句, 编写ResultMap映射
02-Mybatis是如何实现实体类和数据库表映射的 ?
通过注解 : @TableName @TableFiled @TableId @Version
03-Mybatis-Plus自动填充用过嘛 ?
通过@TableFiled中的fill属性可以来指定字段什么时候被自动填充 // 注意!这里需要标记为填充字段 @TableField(fill = FieldFill.INSERT) private String fillField; 还需要自定义实现MyMetaObjectHandler
微服务
SpringBoot
01-SpringBoot自动配置的原理是什么?
SpringBoot启动的时候通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中所有的自动配置类,并对其进行加载,而这些自动配置类的类名都是以AutoConfiguration结尾来命名的,它实际上就是一个javaConfig形式的Spring容器配置类,它们都有一个@EnableConfigurationPerperties的注解,通过这个注解启动XXXProperties命名的类去加载全局配置中的属性,如server.port,而XXXProperties通过@ConfigurationProperties注解将全局配置文件中的属性与自己的属性进行绑定
02-SpringBoot 配置加载顺序?
properties文件 YAML文件 系统环境变量 命令行参数 如果有相同的配置参数, 后加载的会覆盖先加载的
03-spring boot初始化环境变量流程?
调用prepareEnvironment方法去设置环境变量 接下来有三个方法getOrCreateEnvironment,configureEnvironment,environmentPrepared getOrCreateEnvironment去初始化系统环境变量 configureEnvironment去初始化命令行参数 5、 environmentPrepared当广播到来的时候调用onApplicationEnvironmentPreparedEvent方法去使用postProcessEnvironment方法load yml和properties变量
04-运行 SpringBoot 有哪几种方式?
直接使用jar -jar 运行 开发过程中运行main方法 可以配置插件 , 将springboot项目打war包, 部署到Tomcat中运行 直接用maven插件运行 maven spring-boot:run
05-SpringBoot 常用的 Starter 有哪些?
spring-boot-starter-web
spring-boot-starter-jdbc
mybatis-spring-boot-starter
spring-boot-starter-test
mybatis-plus-spring-boot-starter
spring-boot-starter-data-redis
spring-boot-starter-data-elasticsearch
spring-boot-starter-data-mongodb
spring-boot-starter-amqp
spring-cloud-starter-openfeign
spring-cloud-starter-alibaba-nacos-discovery
.....
06-SpringBoot、Spring MVC和Spring有什么区别?
Spring 的完整名字,应该是 Spring Framework 。它提供了多个模块,Spring IoC、Spring AOP、Spring MVC 等等。所以,Spring MVC 是 Spring Framework 众多模块中的一个。 而 Spring Boot 是构造在 Spring Framework 之上的 Boot 启动器,旨在更容易的配置一个 Spring 项目
07-Spring Boot的核心注解是哪个?他由哪几个注解组成的?
@SpringBootConfiguration: 组合了- @Configuration注解,实现配置文件的功能; @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置的功能:@SpringBootApplication(exclude={DataSourceAutoConfiguration.class}); @ComponentScan:Spring组件扫描。
08-Spring Boot 支持哪些日志框架?推荐和默认的日志框架是哪个?
Spring Boot 支持的日志框架有: Logback Log4j2 Log4j Java Util Logging 默认使用的是 Logback 日志框架 , 可以通过logback.xml配置日志格式和级别
09-Spring Boot 提供了哪些核心功能?
快速构建基于Spring的项目 内嵌web服务器, 而且很方便切换 提供了starter简化maven配置以及依赖管理 自动配置SpringBean , 使得配置使用更加简单 1、使【编码】变简单。 2、使【配置】变简单。 3、使【部署】变简单。 4、使【监控】变简单。
10-Spring Boot 有哪些配置方式?
XML配置 注解配置 JavaConfig配置(配置类@Configuration , @Bean)
11-Spring Boot 有哪几种读取配置的方式?
@Value注解读取 , 一般用于少了数据读取 @ConfigrationProperties读取, 一般用于批量读取配置到配置类
12-Jetty 服务器用过嘛 , 如何将内嵌服务器换成 Jetty ?
在web启动器中排出Tomcat依赖 添加jetty服务器的启动器 spring-boot-starter-jetty
SpringCloud
01-什么是 Spring Cloud ?
Spring Cloud 是构建在 Spring Boot 基础之上,用于快速构建分布式系统的通用模式的工具集。里面集成了很多微服务架构下的解决方案 , 例如 : 注册中心, 服务调用 , 服务网关 , 服务保护组件 , 负载均衡组件等
02-你们项目中使用的SpringCloud和SpringCloudAlibaba是什么版本
SpringBoot : 2.3.4.RELEASE SpringCloud : Hoxton.SR10 SpringCloudAlibaba : 2.2.5.RELEASE
03-你们项目中用到Spring Cloud 有哪些组件?
注册中心/配置中心 Nacos
负载均衡 Ribbon
服务调用 Feign
服务保护 sentinel
服务网关 Gateway
......
04-你知道Spring Cloud 和 Spring Boot 的区别和关系吗?
Spring Boot 专注于快速方便的开发单个个体微服务。 Spring Cloud 是关注全局的微服务协调整理治理框架以及一整套的落地解决方案,它将 Spring Boot 开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理,服务发现,断路器,路由,微代理,事件总线等的集成服务。 Spring Boot 可以离开 Spring Cloud 独立使用,但是 Spring Cloud 离不开 Spring Boot ,属于依赖的关系。 总结: Spring Boot ,专注于快速,方便的开发单个微服务个体。 Spring Cloud ,关注全局的服务治理框架。
Nacos
01-Nacos作为配置中心的原理了解过嘛 ?
02-Nacos如何实现环境隔离 , namespace知道嘛 ?
Nacos提供了namespace来实现环境隔离功能。 nacos中可以有多个namespace namespace下可以有group、service等 不同namespace之间相互隔离,例如不同namespace的服务互相不可见 使用方式很简单, 首先需要在Nacos控制台创建命名空间, 然后负责命名空间id(命名空间可以自己指定也可以默认产生UUID) , 在application.yml配置文件中配置 spring: cloud: nacos: server-addr: localhost:8848 discovery: cluster-name: HZ namespace: 492a7d5d-237b-46a1-a99a-fa8e98e4b0f9 # 命名空间,填ID
03-什么是Nacos服务分级存储模型 ?
一个服务可以有多个实例 , 假如这些实例分布于全国各地的不同机房 , Nacos就将同一机房内的实例 划分为一个集群 , 一个服务可以包含多个集群,如杭州、上海,每个集群下可以有多个实例,形成分级模型 实现起来也非常简单, 只需要简单的配置 spring: cloud: nacos: server-addr: localhost:8848 discovery: cluster-name: HZ # 集群名称 因为负载均衡是通过Ribbon实现的 , 需要配置Ribbon的负载均衡策略NacosRule userservice: ribbon: NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则 配置之后微服务互相访问时,会尽可能访问同集群实例,因为本地访问速度更快。当本集群内不可用时,才访问其它集群
04-Nacos如何实现服务的权重配置 ?
在nacos控制台,找到服务的实例列表,点击编辑,即可修改权重 注意:如果权重修改为0,则该实例永远不会被访问
05-Nacos中注册的服务实例类型有哪些 ?
Nacos的服务实例分为两种l类型: 临时实例:如果实例宕机超过一定时间,会从服务列表剔除,默认的类型。 非临时实例:如果实例宕机,不会从服务列表剔除,也可以叫永久实例。 默认我们使用的都是临时实例 , 如果想配置一个服务为非临时实例, 也很简单 spring: cloud: nacos: discovery: ephemeral: false # 设置为非临时实例
06-你知道Eureka嘛 ? Nacos和Eureka有什么区别 ?
Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式 临时实例心跳不正常会被剔除,非临时实例则不会被剔除 Nacos支持服务列表变更的消息推送模式,服务列表更新更及时 Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式
07-你们项目中的配置文件如何管理的 ?
大部分的固定的配置文件都放在服务本地 一些根据环境不同可能会变化的部分, 放到Nacos中
08-项目启动的时候是先加载本地文件还是Nacos中的文件 ?
在本地文件中会配置Nacos配置中心地址, 所以肯定是先加载本地配置文件, 连接上Nacos再加载配置中心中的文件
09-如何实现配置的热更新 , 配置改变的情况下不修改源码即可实现配置的更新 ?
我们使用Nacos作为配置中心, 实现热更新非常简单, 有二种方式 ; 在需要热更新的类上添加@RefreshScope注解, 使用@Value注解读取需要更新的配置 使用@ConfigurationProperties注解代替@Value注解 , 可以直接实现热更新 当配置方式改变的时候, Nacos会主动向服务推送改变的内容
Ribbon
01-Ribbon是如何实现负载均衡的 ?
02-Ribbon支持的负载均衡策略有哪些 ?
RoundRobinRule AvailabilityFilteringRule WeightedResponseTimeRule ZoneAvoidanceRule BestAvailableRule RandomRule RetryRule
03-Ribbon默认的负载均衡策略是哪种 ? 如何修改Ribbon的负载均衡策略 ?
Ribbon默认的负载均衡策略是ZoneAvoidanceRule , 没有跨区域机房的情况相当于随机 修改Ribbon负载均衡策略的方式有二种 : 1.在配置类中配置IRule @Bean public IRule randomRule(){ return new RandomRule(); } 2.在application.yml配置文件中配置 ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
04-了解过如何自定义负载均衡策略嘛 ?
Ribbon自定义负载均衡也很简单 , 自己创建类实现IRule接口 , 实现里面的choose方法即可 , 至于具体的负载均衡规则如何做是什么就要看具体的需求了
Feign
01-什么是Feign?
Feign 是一个声明web服务客户端,这使得编写web服务客户端更容易 他将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的映射路径 , 参数和返回类型一致。
02-Feign的服务调用和Dubbo有什么区别 ?
feign是基于HTTP协议的远程调用 Dubbo是基于RPC的远程调用
03-使用Feign调用服务过程中超时怎么办 ?
1. 可以调整feign的超时时间 ConnectTimeout ReadTimeout
04-使用Feign进行远程调用, 如何实现负载均衡 ?
Feign底层通过Ribbon实现负载均衡
05-如何提高Feign的服务调用效率 ?
Feign底层采用URLConnection发起远程调用, 默认不支持连接池 我们可以修改底层实现使用OkHttp , HttpClient 实现远程调用 , 这样连接可以回收 , 提升了效率 具体做法就是 添加feign-httpclient依赖 配置使用HttpClient feign: client: httpclient: enabled: true # 开启feign对HttpClient的支持 max-connections: 200 # 最大的连接数 max-connections-per-route: 50 # 每个路径的最大连接数
Gateway
01-在你们的项目中用到了网关的哪些功能 ?
路由
跨域
鉴权
限流
02-你在开发过程中经常使用的路由断言有哪些 ?
server: port: 8888 spring: application: name: tanhua-gateway cloud: nacos: discovery: server-addr: 192.168.136.160:8848 gateway: globalcors: add-to-simple-url-handler-mapping: true corsConfigurations: '[/**]': allowedHeaders: "*" allowedOrigins: "*" allowedMethods: - GET - POST - DELETE - PUT - OPTION routes: # app-server服务 - id: tanhua-app-server uri: lb://tanhua-app-server predicates: - Path=/app/** filters: - StripPrefix= 1 # 跳过前缀 # 管理微服务 - id: tanhua-admin uri: lb://tanhua-admin predicates: - Path=/admin/** filters: - StripPrefix= 1
Path : 根据请求路径匹配
Before/After : 根据时间路由 , 一般用于新版本上线
Header : 根据请求头路由, 一般用户灰度发布
03-你们在开发中经常用到的过滤器有哪些 ?
AddRequestHeader : 添加请求头 , 用户sentinel黑白名单
RequestRateLimiter : 限流配置
StripPrefix : 取消路径前缀
04-有没有使用过网关的全局过滤器 ?
使用过, 主要通过自定义全局过滤器实现对请求的统一权限校验 客户端发送请求携带token到网关, 由网关负责统一的token解析, 解析完毕之后获取token中的用户信息, 保存到请求头中, 路由到微服务
Sentinal
01-了解过服务雪崩嘛 ?
微服务中,服务间调用关系错综复杂,一个微服务往往依赖于多个其它微服务 , 如果其中一个下游服务出现了问题 , 或者服务器负载比较高 , 处理缓慢, 这个时候就会导致上游服务等待 , 线程得不到释放 , 最后导致服务器资源耗尽宕机, 然后他的上游服务同样如此, 这样因为一个服务导致上游的所有服务都出现问题的线下称之为雪崩
02- 你有了解过如何解决雪崩问题吗 ?
超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待 线程隔离/信号量隔离 熔断降级
03-了解过Hystrix嘛, Sentinal和Hystrix有什么区别 ?
04-你们项目中是如何使用Sentinal的 ?
sentinel使用非常简单 启动sentinel服务端 在项目找那个引入sentinel启动器 在application.yml中配置sentinel地址 在sentinel中配置各种控制规则
05-sentinel支撑的流控模式有哪些 ?
直接: 统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式 关联: 统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流 链路: 统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流
06-sentinel支持的流控效果有哪些 ?
流控效果是指请求达到流控阈值时应该采取的措施,包括三种: 快速失败: 达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。 warm up: 预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。 排队等待: 让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长
07-sentinel是否可以实现对指定热点数据访问限流 ?
可以, sentinel支持热点参数限流 , 具体就是分别统计参数值相同的请求,判断是否超过QPS阈值。 具体就是需要在控制面板配置热点参数和阈值
08-sentinel中如何对一个普通的方法限流 ?
想要使用sentinel限流 , 必须把方法标注为一个sentinel资源, 通过@SentinalResource注解来标注方法即可
09-sentinel断路器知道嘛 ? 他是怎么工作的 ?
状态机包括三个状态: - closed: 关闭状态,断路器放行所有请求,并开始统计异常比例、慢请求比例。超过阈值则切换到open状态 - open:打开状态,服务调用被熔断 ,访问被熔断服务的请求会被拒绝,快速失败,直接走降级逻辑。Open状态5秒后会进入half-open状态 half-open: 半开状态,放行一次请求,根据执行结果来判断接下来的操作。请求成功:则切换到closed状态请求失败:则切换到open状态
10-sentinel什么情况下会触发熔断降级 ?
断路器熔断策略有三种:慢调用、异常比例、异常数 , 可以在sentinel控制台中配置
11-sentinel限流底层是通过什么机制实现的 ?
线程池隔离 :给每个服务调用业务分配一个线程池,利用线程池本身实现隔离效果 信号量隔离(默认使用) :不创建线程池,而是计数器模式,记录业务使用的线程数量,达到信号量上限时,禁止新的请求。
12-sentinel如何实现对访问来源的控制 ?
sentinel可以通过 授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式。 白名单:来源(origin)在白名单内的调用者允许访问 黑名单:来源(origin)在黑名单内的调用者不允许访问 主要做法就是 : 实现RequestOriginParser接口 , 从request对象中,获取请求者的origin值并返回
13-sentinel中配置的规则是如何保存的 ? 重启之后还生效嘛 ?
现在,sentinel的所有规则都是内存存储,重启后所有规则都会丢失。在生产环境下,我们必须确保这些规则的持久化,避免丢失。 规则是否能持久化,取决于规则管理模式,sentinel支持三种规则管理模式: - 原始模式: Sentinel的默认模式,将规则保存在内存,重启服务会丢失。 pull模式 : 控制台将配置的规则推送到Sentinel客户端,而客户端会将配置规则保存在本地文件或数据库中。以后会定时去本地文件或数据库中查询,更新本地规则 push模式 : 控制台将配置规则推送到远程配置中心,例如Nacos。Sentinel客户端监听Nacos,获取配置变更的推送消息,完成本地配置更新。 开源的sentinel版本中不支持push模式, 需要修改源码实现
Redis
01-Redis中有哪些数据结构(类型) ?
Redis 有 5 种基础数据结构,它们分别是:string(字符串)、list(列表)、hash(字典)、set(集 合) 和 zset(有序集合)
02-Redis 和 Memcached 的区别有哪些?
1. Redis 支持复杂的数据结构 Memcached 仅提供简单的字符串。 Redis 提供复杂的数据结构,丰富的数据操作 2. Redis原生支持集群模式 , Memcached不支持原生集群 主从复制集群 哨兵集群 Cluster集群 3. 网络IO模型不同 Memcached 是多线程,非阻塞 IO 复用的网络模型 Redis 使用单线程的 IO 复用模型 4. 持久化存储 Memcached 不支持持久化存储,重启时,数据被清空。 Redis 支持持久化存储,重启时,可以恢复已持久化的数据
03-为什么 Redis 单线程模型也能效率这么高?
1. C 语言实现。 2. 纯内存操作。 Redis 为了达到最快的读写速度,将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以 Redis 具有快速和数据持久化的特征。 如果不将数据放在内存中,磁盘 I/O 速度为严重影响 Redis 的性能。 3. 基于非阻塞的 IO 多路复用机制。 4. 单线程,避免了多线程的频繁上下文切换问题。 Redis 利用队列技术,将并发访问变为串行访问,消除了传统数据库串行控制的开销。 5. 丰富的数据结构。
04-Redis 有几种持久化方式?如何选择 ?
Redis 提供了两种方式,实现数据的持久化到硬盘。 1. RDB 持久化(全量),是指在指定的时间间隔内将内存中的数据集快照写入磁盘。 2. AOF持久化(增量),以日志的形式记录服务器所处理的每一个写、删除操作 RDB和AOF一起使用, 在Redis4.0版本支持混合持久化方式
05-Redis支持事务吗 ?
Redis 作为 NoSQL 数据库也同样提供了事务机制。在 Redis 中,MULTI / EXEC / DISCARD / WATCH 这四个命令是我们实现事务的基石
06-Redis 有几种数据“过期”策略?
Redis 提供了 3 种数据过期策略: 惰性删除:当读/写一个已经过期的 key 时,会触发惰性删除策略,直接删除掉这个过期 key 。 定期删除:由于惰性删除策略无法保证冷数据被及时删掉,所以 Redis 会定期主动淘汰一批已过期的 key 。 主动删除:当前已用内存超过 maxmemory 限定时,触发主动清理策略,即 「数据“淘汰”策略」 。 在 Redis 中,同时使用了上述 3 种策略,即它们非互斥的。
07-Redis 有哪几种数据“淘汰”策略?
Redis 内存数据集大小上升到一定大小的时候,就会进行数据淘汰策略。 Redis 提供了 6 种数据淘汰策略: volatile-lru volatile-ttl volatile-random allkeys-lru allkeys-random 【默认策略】no-enviction 在 Redis 4.0 后,基于 LFU(Least Frequently Used)最近最少使用算法,增加了 2 种淘汰策略: volatile-lfu allkeys-lfu
08-如果有大量的 key 需要设置同一时间过期,一般需要注意什么?
如果大量的 key 过期时间设置的过于集中,到过期的那个时间点,Redis可能会出现短暂的卡顿现象。 1.一般需要在时间上加一个随机值,使得过期时间分散一些。 2.调大 hz 参数,每次过期的 key 更多,从而最终达到避免一次过期过多。
09-Redis 集群都有哪些方案?
我所了解的Redis集群方案 : 主从复制集群 哨兵集群 Cluster集群
10-什么是 Redis 主从同步?
Redis 的主从同步(replication)机制,允许 Slave 从 Master 那里,通过网络传输拷贝到完整的数据备份,从而达到主从机制。 主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的,并接收主数据库同步过来的数据。 一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。 第一次同步时,主节点做一次 bgsave 操作,并同时将后续修改操作记录到内存 buffer ,待完成后将 RDB 文件全量同步到复制节点,复制节点接受完成后将 RDB 镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程
11-说说 Redis 哈希槽的概念?
Redis Cluster 没有使用一致性 hash ,而是引入了哈希槽的概念。 Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽,集群的每个节点负责一部分 hash 槽。 因为最大是 16384 个哈希槽,所以考虑 Redis 集群中的每个节点都能分配到一个哈希槽,所以最多支持 16384 个 Redis 节点。 为什么是 16384 呢?主要考虑集群内的网络带宽,而 16384 刚好是 2K 字节大小。
12-Redis Cluster 的主从复制模型是怎样的?
为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制 模型,每个节点都会有 N-1 个复制节点。 所以,Redis Cluster 可以说是 Redis Sentinel 带分片的加强版。也可以说: Redis Sentinel 着眼于高可用,在 master 宕机时会自动将 slave 提升为 master ,继续提供服务。 Redis Cluster 着眼于扩展性,在单个 Redis 内存不足时,使用 Cluster 进行分片存储。
13-Redis Cluster 会有写操作丢失吗?为什么?
Redis 并不能保证数据的强一致性,而是【异步复制】,这意味这在实际中集群在特定的条件下可能会丢失写操作。
14-Redis 有哪些重要的健康指标?
存活情况 连接数 阻塞客户端数量 使用内存峰值 内存碎片率 缓存命中率 OPS 持久化 失效KEY 慢日志
15-假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?会有声明问题
使用 keys 指令可以扫出指定模式的 key 列表。
16-使用Redis统计网站的UV(独立访客数),应该怎么做?
UV(独立访客数)与PV(访问量 )不同,UV需要去重。一般有2种方案: 1、用BitMap。存的是用户的uid,计算UV的时候,做下bitcount就行了。 2、用布隆过滤器。将每次访问的用户uid都放到布隆过滤器中。优点是省内存,缺点是无法得 到精确的UV。但是对于不需要精确知道具体UV,只需要大概的数量级的场景,是个不错的选 择。
17-Redis中的大key怎么处理?
大key指的是value特别大的key。比如很长的字符串,或者很大的set等等。 大key会造成2个问题: 数据倾斜,比如某些节点内存占用过高。 当删除大key或者大 key自动过期的时候,会造成QPS突降,因为Redis是单线程的缘故。 处理方案:可以将一个大key进行分片处理,比如:将一个大set分成多个小的set。
18-缓存雪崩(缓存失效)了解过嘛 ?
缓存失效 缓存失效指的是大量的缓存在同一时间失效,到时DB的瞬间压力飙升。造成这种现象的 原因是,key的过期时间都设置成一样了。 解决方案是,key的过期时间引入随机因素, 比如5分钟+随机秒这种方式
19-缓存穿透了解过嘛 ?
缓存穿透是指查询一条数据库和缓存都没有的一条数据,就会一直查询数据库,对数据 库的访问压力就会增大,缓存穿透的解决方案,有以下2种: 缓存空对象:代码维护较简单,但是效果不好。 布隆过滤器:代码维护复杂,效果很好
20-缓存击穿了解过嘛 ?
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力 解决方案 : 热点数据提前预热 设置热点数据永远不过期。 加锁 , 限流
21-缓存并发了解过嘛 ?
有时候如果网站并发访问高,一个缓存如果失效,可能出现多个进程同时查询DB,同时 设置缓存的情况,如果并发确实很大,这也可能造成DB压力过大,还有缓存频繁更新的 问题。 一般处理方案是在查DB的时候进行加锁,如果KEY不存在,就加锁,然后查DB入缓存, 然后解锁;其他进程如果发现有锁就等待,然后等解锁后再查缓存或者进入DB查询
ElasticSearch
原理
查询方式
分片
路由
脑裂
深度搜索
RabbitMQ
RabbitMQ工作模式
死信交换机
延迟队列
惰性队列
可靠性保证
Kafka
工作流程
相关概念
可靠性保证
其他
xxl-job
fastdfs
环信
阿里云盾安全