Java面试复习
2021-02-18 17:13:49 13 举报
AI智能生成
登录查看完整内容
Java面试复习主要包括对Java基础知识的掌握,如数据类型、运算符、流程控制、类和对象、继承和多态等。同时,也需要熟悉Java的异常处理机制,理解并能够使用Java的集合框架。此外,对于Java的内存模型、垃圾回收机制、多线程编程等高级主题也要有所了解。在复习过程中,可以通过刷题来提高编程能力和解决问题的能力。同时,也要关注实际项目中的应用,理解和掌握常用的设计模式和架构模式。最后,面试时要注意展示自己的逻辑思维能力,清晰、准确地表达自己的观点。
作者其他创作
大纲/内容
⾯向过程性能⽐⾯向对象⾼。 因为类调⽤时需要实例化,开销比较⼤,比较消耗资源,所以当性能是最重要的考量因素的时候,⽐如单⽚机、嵌⼊式开发、Linux/Unix 等⼀般采⽤⾯向过程开发。但是⾯向过程没有⾯向对象易维护、易复⽤、易扩展。
⾯向过程
⾯向对象易维护、易复⽤、易扩展。
⾯向对象
⾯向对象和⾯向过程的区别?
抽象类可以有构造函数,抽象方法不能被声明为静态。
抽象类不能被实例化
java抽象类和普通类的区别?
private 私有成员属性和方法 只有本类可以调用,除内部类特殊情况
默认不写 只有本类 同一个包下面的类
protected 本类 同包 不同包的子类
public 本类 同包 不同包的类
Java 方法访问权限修饰?
成员内部类可以无条件访问外部类所有的成员属性和成员方法(包括private成员)
外部类.this.成员变量
外部类.this.成员方法
如果要访问外部类的同名成员,需要以下方式
在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问
第一种方式:Outter outter = new Outter();Outter.Inner inner = outter.new Inner();//必须通过Outter对象来创建
第二种方式:Outter.Inner inner1 = outter.getInnerInstance();
要创建成员内部类的对象,前提是必须存在一个外部类的对象
内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限
成员内部类不允许 static修饰 属性 和 方法
成员内部类
静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法
向外访问 只能直接访问static修饰属性和方法,同名用 Outter.str2。
非静态的外部属性方法需要实例化外部对象调用
可以有 main方法
静态内部类
不允许使用访问权限修饰符该类堆方法以外的全部隐藏,除了此方法其他的都不能访问
方法内部类
匿名内部类必须继承一个抽象类或者实现一个接口。
匿名内部类没有类名,因此没有构造方法。匿名内部类是唯一一种没有构造器的类
匿名内部类
内部类种类
背景: 当一个方法执行完成之后,局部变量的生命周期也就结束了,而此时Thread对象的生命周期很可能还没有结束,那么在Thread的run方法中继续访问这个变量就不可能了。但是又必须需要这个变量,怎么办呢?Java采用了复制的手段来解决这个问题造器传参的方式来对拷贝进行初始化赋值。
方案∶也就说如果局部变量的值在编译期间就可以确定,则直接在匿名内部里面创建一个拷贝。如果局部变量的值无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。
问题∶当在run方法中改变变量a的值的话,会造成数据不一致性
解决:java编译器就限定必须将入参变量限制为final变量
为什么局部内部类和匿名内部类只能访问局部final变量?
一个内部类的对象能够访问创建它的对象的实现,包括私有数据
对于同一个包中的其他类来说,内部类能够隐藏起来
匿名内部类可以很方便的定义回调
使用内部类可以非常方便的编写事件驱动程序。
在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类
能够非常好的解决多重继承的问题
内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独
内部类的有点
深入理解内部类
Java内部类
String类底层原理?
String为什么保证不可变?
使用 + 连接符 来进行String的拼接原理?
用常量字符串赋值给String引用 和 用new来创建字符串对象 的区别?
String
StringBuilder
StringBuffer
字符串常量池在哪儿?
JDK1.7 及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池。
Java 字符串常量存放在堆内存还是JAVA方法区?
创建对象的过程 1 首先在常量池中查找是否存在内容为\"abc\"字符串对象 2 如果不存在则在常量池中创建\"abc\",并让str引用该对象 3 如果存在则直接让str引用该对象
String str= \"abc\" 创建方式
创建对象的过程 1 首先在堆中(不是常量池)创建一个指定的对象,并让str引用指向该对象。 2 在字符串常量池中查看,是否存在内容为\"abc\"字符串对象 3 若存在,则将new出来的字符串对象与字符串常量池中的对象联系起来(即让那个特殊的成员变量value的指针指向它) 4 若不存在,则在字符串常量池中创建一个内容为\"abc\"的字符串对象,并将堆中的对象与之联系起来。(有可能此时常量池中的\"abc\"已经被回收,所以要先创建一个内容 为\"abc\"的字符串对象)
str 放在栈上,用 new 创建出来的字符串对象放在堆上,而 abc 这个字面量是放在常量池中。
String str= new String(\"abc\")创建方式
String创建对象
字符串
内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
代码中存在死循环或循环产生过多重复的对象实体
启动参数内存值设定的过小
内存溢出 没有足够的内存可以使用
内存泄漏 强引用对象没有没有释放使用的空间长时间的堆积造成
内存溢出和内存泄漏的区别
值传递:是指在调用函数时,将实际参数复制一份传递给函数,这样在函数中修改参数时,不会影响到实际参数
引用传递:是指在调用函数时,将实际参数的地址传递给函数,这样在函数中对参数的修改,将影响到实际参数
然后 Java中 将一个值传递到方法中是不会影响原的值的,是通过复制的方式。\t\t1. 基本数据类型 放在栈中 每次赋值后都不会影响原来的值\t\t2. 引用数据类型 复制的是栈中的 指向堆 对象的地址 或者 句柄 现在指向的和原来指向的都是同一个对象。 你改了这个对象的值\t\t3. String类型 string 每次 new 改变对象
为什么 Java 中只有值传递?
ArithmeticException(算术异常)ClassCastException (类转换异常)IllegalArgumentException (非法参数异常)IndexOutOfBoundsException (下标越界异常)NullPointerException (空指针异常)SecurityException (安全异常)
常见异常?
异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生。受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。Java 编译器要求方法必须声明抛出可能发生的受检异常,但是并不要求必须声明抛出未被捕获的运行时异常。
运行时异常与受检异常有何异同?
里氏代换原则[能使用父类型的地方一定能使用子类型]
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
对接口的要求:Lambda 规定接口中只能有一个需要被实现的方法.
语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)。
Lambda 表达式
方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
Date Time API − 加强对日期与时间的处理。
Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
JDK1.8新特性?
封装 继承 多态 抽象类 接口
面向对象特点?
Java 程序如打日志?
注解
反射
基础
字符流是由JVM将字节流抓换得到的。方便我们平时对 字符 进行流操作 \t\t\t如果是 图片 视屏 音频 等媒体文件用字节流 如果是字符 用字符流操作
IO
存取有序,元素可以重复,有序就有索引,有索引就可以通过索引操作元素。遍历方式:普通for,增强for,Iterator,ListIterator,集合转数组
不安全,效率高,数组结构:增删慢;查询快
ArrayList
不安全,效率高,链表结构:增删快;查询慢
LinkedList
数组结构 安全 效率低
存取无序,元素唯一。遍历方式:增强for,Iterator,集合转数组
Vector
List
哈希算法 哈希结构 存取无序 元素唯一
HashSet
二叉树算法可以排序
TreeSet
Set
Collection
双列集合,键唯一,值可以重复,遍历方式:根据键找值,根据键值对找键和值
⾮线程安全 null可以作为键 初始值16 原容量的2倍 数组+链表/红⿊⼆叉树 适用于在Map中插入、删除和定位元素
HashMap
线程安全 null不可以作为键 初始值11 原容量2倍加1 数组+链表
HashTable
基于红黑树实现 适用于按自然顺序或自定义顺序遍历键(key)。
TreeMap
LinkedHashMap
将原先table数组+单向链表的数据结构,变更为table数组+单向链表+红黑树的结构.
改进
默认初期长度为16,当往map中继续添加元素的时候,通过hash值跟数组长度取与来决定放在数组的哪个位置,如果出现放在同一个位置的时候,优先以链表的形式存放,在同一个位置的个数又达到了8个以上,如果数组的长度还小于64的时候,则会扩容数组。如果数组的长度大于等于64了的话,在会将该节点的链表转换成树。
如果某个节点的是树,同时现在该节点的个数又小于等于6个了,则会将该树转为链表
原理
ConcurrentHashMap
Map
集合
Java实现跨平台的原理就是 一次编译,到处运行 将源文件编译成class文件。 只有提供并且安装了相对应的虚拟机就可以跨该平台
遇到内存溢出可以知道什么原因知道代码运行原理,提高代码执行效率JVM调优
了解JVM的好处
当前线程所执⾏的字节码解释器。⼯作时通过改变这个计数器的值来选取下一条需要执行的字节码指令实现代码流程控制,分⽀、循环、跳转、异常处理、保证每条线程切换后可以恢复到正确的位置,每条线程在方法执行时都需要一个私有的计数器,相互独立,互不干扰。
程序计数器
在JVM运行时存放局部变量、 对象引⽤地址、方法参数的内存区域。每一个方法在被调用时都对应一个栈帧内存区,⽽每个栈帧中都拥有:局部变量表、操作数栈、动态链接、⽅法出⼝信息。执行完就释放空间
用来存放 局部变量 方法参数8种基本数据类型对象引用ReturnAddress 返回 字节码地址
局部变量表
在一步细化后的栈 就是方法中的 计算操作需要的临时空间,也是栈结构(先进后出)列如:加减乘除
存储的数据与局部变量表一致含int、long、float、double、reference、returnType,操作数栈中byte、short、char压栈前(bipush)会被转为int。
操作数栈
动态链接
记录方法被调用返回的位置
⽅法出⼝信息
虚拟机栈
本地方法栈
主要存放的是 类信息、常量、静 态变量、即时编译器编译后的代码等数据
在JDK1.7之前存在 其实方法去属于 堆的逻辑部分 但 是它却有⼀个别名叫做 Non-Heap(⾮堆),⽬的应该是与 Java 堆区分开来
⽅法区和永久代的关系很像Java中接⼝和类的关系,类实现了接⼝,⽽永久代就是HotSpot虚拟机对虚拟机规范中⽅法区的⼀种实现⽅式。 也就是说,永久代是HotSpot的概念,⽅法区是Java虚拟机规范中的定义,是⼀种规范,⽽永久代是⼀种实现,⼀个是标准⼀个是实现,其他的虚拟机实现并没有永久带这⼀说法。
方法区和永久代的区别
JDK 1.8 的时候,⽅法区(HotSpot的永久代)被彻底移除了(JDK1.7就已经开始了),取⽽代之是元空间,元空间使⽤的是直接内存。
-XX:MetaspaceSize=N //设置Metaspace的初始(和最⼩⼤⼩)-XX:MaxMetaspaceSize=N //设置Metaspace的最⼤⼤⼩
为什么移除永久代? 有了元空间就不再会出现永久代OOM问题了
元空间
方法区(永久代)
运⾏时常量池是⽅法区的⼀部分,JDK1.7及之后版本的 JVM 已经将运⾏时常量池从⽅法区中移了出来,在 Java 堆(Heap)中开辟了⼀块区域存放运⾏时常量池。
文本字符串
被声明为final的常量值
基本数据类型的值
字面量
类和结构的完全限定名
字段名称和描述符
方法名称和描述符
符号引用
常量池包含的内容
运⾏时常量池
Java 虚拟机所管理的内存中最⼤的⼀块,此内存区域的唯⼀⽬的就是存放对象实例,⼏乎所有的对象实例以及数组都在这⾥分配内存
新生代 (Eden)——>幸存者S0(From Survivor) <——> 幸存者S1(To Survivor)——>老年代(tentired)
年轻代 Minor GC 清理年轻代 三部分的8:1:1。 经过多次Minor GC 还存活的对象(计数器年龄达到15)被移到老年代
老年代 Mejor GC 清理老年代。老年代存储长期存活的对象,占满时会触发Major GC=Full GC
Major GC=Full GC: 清理整个堆空间,包括年轻代和永久代。GC期间会停止所有线程等待GC完成(STW stop work)
为什么分代?将对象根据存活概率进行分类,对存活时间长的对象,放到固定区从而减少扫描垃圾时间及GC频率。针对分类进行不同的垃圾回收算法,对算法扬长避短。
堆 Heap
JVM 模型及功能
线程私有
线程共享
对象在新生代中 Eden 区分配。当 Eden 区没有足够空间进行分配时,虚拟机将发起一次Minor GC。
对象优先在Eden区分配
如果对象超过设置大小会直接进入老年代,不会进入年轻代
大对象直接进入老年代
多次Miner GC后存活下来的对象
长期存活的对象将进入老年代
对象动态年龄判断
这种情况会把存活的对象部分挪到老年代,部分可能还会放在Survivor区
Minor gc后存活的对象Survivor区放不下
老年代空间分配担保机制
Eden与Survivor区默认8:1:1
JVM内存分配与回收
算法逻辑是这样的:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加 1;当引用失效时,计数器就减 1;任何时刻计数器为 0 的对象就是不可能再被使用的
引用计数算法
可达性分析算法
指在程序代码之中普遍存在的,类似 “Object obj = new Object()” 这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
强引用
软引用:用来描述一些还有用但并非必须的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列入回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。JDK1.2 之后,提供了 SoftReference 类来实现软引用。
软引用
弱引用:也是用来描述非必须对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾回收发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。JDK1.2 之后,提供了 WeakReference 类来实现弱引用。
弱引用
虚引用:是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。JDK1.2 之后,提供了 PhantomReference 类来实现虚引用。
虚引用
引用分类
即使在可达性分析算法中不可达的对象,也并非是 “非死不可”的,这时候它们暂时处于 “缓刑” 阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize() 方法。当对象没有覆盖 finalize() 方法,或者 finalize() 方法已经被虚拟机调用过,虚拟机将这两种情况都视为 “没有必要执行”。 如果这个对象被判定为有必要执行 finalize() 方法,那么这个对象将会放置在一个叫做 F-Queue 的队列之中,并在稍后由一个虚拟机自动创建的、低优先级的 Finalizer 线程去执行它。这里的 “执行” 是指虚拟机会触发这个方法,但不承诺会等待它运行结束,这样做的原因是,如果一个对象在 finalize() 方法中执行缓慢,或者发生死循环(更极端的情况),将很可能会导致 F-Queue 队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。finalize() 方法是对象逃脱死亡命运的最后一次机会,稍后 GC 将对 F-Queue 中的对象进行第二次小规模的标记,如果对象要在 finalize() 中成功拯救自己,只要重新与引用链上的任何一个对象建立关联即可,比如在自己(this 关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移除出 “即将回收” 的集合;如果对象这个时候还没有逃脱,那基本上它就真的被回收了。
生存还是死亡?
如何判断对象可以被回收?
GC分为两个阶段,标记和清除。首先标记所有可回收的对象,在标记完成后统一回收所有被标记的对象。同时会产生不连续的内存碎片。碎片过多会导致以后程序运行时需要分配较大对象时,无法找到足够的连续内存,而不得已再次触发GC。
标记-清除(Mark-Sweep)
将内存按容量划分为两块,每次只使用其中一块。当这一块内存用完了,就将存活的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。这样使得每次都是对半个内存区回收,也不用考虑内存碎片问题,简单高效。缺点需要两倍的内存空间。
复制算法(Copy)
也分为两个阶段,首先标记可回收的对象,再将存活的对象都向一端移动,然后清理掉边界以外的内存。此方法避免标记-清除算法的碎片问题,同时也避免了复制算法的空间问题。一般年轻代中执行GC后,会有少量的对象存活,就会选用复制算法,只要付出少量的存活对象复制成本就可以完成收集。而老年代中因为对象存活率高,没有额外过多内存空间分配,就需要使用标记-清理或者标记-整理算法来进行回收。
标记-整理算法(Mark-Compact)
现在的商用虚拟机的垃圾收集器基本都采用\"分代收集\"算法,这种算法就是根据对象存活周期的不同将内存分为几块。一般将java堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
在新生代中,每次收集都有大量对象死去,所以可以选择复制算法,只要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率时比较高的,而且没有额外的空间对它进行分配担保,就必须选择“标记-清除”或者“标记-整理”算法进行垃圾收集
分代收集算法
垃圾回收算法
比较老的收集器,单线程。收集时,必须暂停应用的工作线程,直到收集结束。
串行收集器(Serial)
多条垃圾收集线程并行工作,在多核CPU下效率更高,应用线程仍然处于等待状态。
并行收集器(Parallel)
CMS收集器是缩短暂停应用时间为目标而设计的,是基于标记-清除算法实现,整个过程分为4个步骤,包括:初始标记(Initial Mark)并发标记(Concurrent Mark)重新标记(Remark)并发清除(Concurrent Sweep)其中,初始标记、重新标记这两个步骤仍然需要暂停应用线程。初始标记只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段是标记可回收对象,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作导致标记产生变动的那一部分对象的标记记录,这个阶段暂停时间比初始标记阶段稍长一点,但远比并发标记时间段。由于整个过程中消耗最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,CMS收集器内存回收与用户一起并发执行的,大大减少了暂停时间。
CMS收集器(Concurrent Mark Sweep)
G1收集器将堆内存划分多个大小相等的独立区域(Region),并且能预测暂停时间,能预测原因它能避免对整个堆进行全区收集。G1跟踪各个Region里的垃圾堆积价值大小(所获得空间大小以及回收所需时间),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region,从而保证了再有限时间内获得更高的收集效率。G1收集器工作工程分为4个步骤,包括:初始标记(Initial Mark)并发标记(Concurrent Mark)最终标记(Final Mark)筛选回收(Live Data Counting and Evacuation)初始标记与CMS一样,标记一下GC Roots能直接关联到的对象。并发标记从GC Root开始标记存活对象,这个阶段耗时比较长,但也可以与应用线程并发执行。而最终标记也是为了修正在并发标记期间因用户程序继续运作而导致标记产生变化的那一部分标记记录。最后在筛选回收阶段对各个Region回收价值和成本进行排序,根据用户所期望的GC暂停时间来执行回收。
G1收集器(Garbage First)
垃圾收集器
方法区主要回收的是无用的类,判断类无用同时满足三个条件
该类所有的实例都已经被回收,也就是 Java堆中不存在该类的任何实例。加载该类的 ClassLoader 已经被回收。该类对应的 java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
如何判断一个类是无用的类?
假如在常量池中存在字符串 \"abc\",如果当前没有任何String对象引⽤该字符串常量的话,就说明常量\"abc\" 就是废弃常量,如果这时发⽣内存回收的话⽽且有必要的话,\"abc\" 就会被系统清理出常量池。
如何判断⼀个常量是废弃常量?
GC垃圾回收
jvisualvm java 虚拟机诊断工具 Visual VM插件
减少 full GC过程中的 STW(stop work)时间
减少 full GC次数
JVM调优的目的是?
能否对JVM调优,让其几乎不发生full gc?
JVM 调优
描述一下 JVM 加载 class 文件的原理机制?
类加载检查
分配内存
初始话零值
设置对象头\t
执⾏ init ⽅法
说⼀下Java对象的创建过程
JVM
一个类中只有一个实例对象,构造器是被private修饰。
单例模式
提供一个用于创建对象的接口(工厂接口),让其实现类(工厂实现类)决定实例化哪一个类(产品类),并且由该实现类创建对应类的实例。
工厂模式
静态代理
动态代理
Cglib代理
代理模式
对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式
builder模式
生产者/消费者模式
设计模式
Java SE
一个线程在本地内存中修改了共享内存的数据,对于其他持有该数据的线程是“可见”的。用volatile修饰的变量,就会具有可见性
可见性
sychronized 保证原子性 不可分割,同生共死。非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。
原子性
此规则决定了持有同一个对象锁的两个同步块只能串行执行。volatile 和 synchronized 两个关键字来保证线程之间操作的有序性
有序性
基本概念
Java线程 计算操作
工作内存(高速缓存 变量副本)
缓存一致性协议(总线嗅探机制)
主内存
read 读取 从主内存中读取数据
load 载入 将从主内存中读取的数据加载到工作内存的副本中
use 使用 将工作内存中的数据做计算操作
assign 赋值 将计算结果值赋值到工作内存中
store 存储 将工作内存的值 赋值到主内存
write 写入 将store中的变量赋值到主内存的变量中
lock 加锁 将主内存中的变量锁 表示变量被线程独占状态
unlock 解锁 将主内存中的变量解锁 解锁后其他线程可以锁定
原子操作
内存模型结构
Java内存模型
进程是程序的⼀次执⾏过程,是系统运⾏程序的基本单位,因此进程是动态的
线程是⼀个⽐进程更⼩的执⾏单位。⼀个进程在其执⾏的过程中可以产⽣多个线程
什么是线程和进程?
1. 字节码解释器通过改变程序计数器来依次读取指令,从⽽实现代码的流程控制,如:顺序执⾏、选择、循环、异常处理。
2.通过字节码解释器的地址,线程切换回来可以知道上一次执行到哪了
程序计数器为什么是私有的?
每个 Java ⽅法在执⾏的同时会创建⼀个栈帧⽤于存储局部变量表、操作数栈、常量池引⽤等信息。从⽅法调⽤直⾄执⾏完的过程,就对应着⼀个栈帧在 Java 虚拟机栈中⼊栈和出栈的过程
本地⽅法栈则为虚拟机使⽤到的 Native ⽅法服务
本地⽅法栈
虚拟机栈和本地⽅法栈为什么是私有的?
并发:同⼀时间段,多个任务都在执⾏ (单位时间内不⼀定同时执⾏) A 执行一段时间 让B执行一段时间
并行:单位时间内有多个任务在执行 A B 同事执行
说说并发与并⾏的区别?
现在的系统动不动就要求百万级甚⾄千万级的并发量,⽽多线程并发编程正是开发⾼并发系统的基础,利⽤好多线程机制可以⼤⼤提⾼系统整体的并发能⼒以及性能
提高CPU的利用率 目前大多数CPU都是多核的 可以都利用起来
守护线程的优先级很低 GC就是一个经典的守护线程
为什么要使⽤多线程呢?
在执行new Thread(s),线程对象一旦创建就进入新生状态
新生状态
在线程对象创建完成后调用start方法,但是线程不会立刻调度执行,而是进入就绪状态,因为在运行前还有一些准备工作要做
就绪状态
线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法
运行状态
waiting sleep 超时等待 time waiting 正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态
阻塞状态
①run方法正常退出而自然死亡;②一个未捕获的异常终止了run方法而使线程猝死
终止状态 dead
说说线程的⽣命周期和状态?
并发编程的⽬的就是为了能提⾼程序的执⾏效率提⾼程序运⾏速度,但是并发编程并不总是能提⾼程序运⾏速度的,⽽且并发编程可能会遇到很多问题,⽐如:内存泄漏、上下⽂切换、死锁还有受限于硬件和软件的资源闲置问题。
使⽤多线程可能带来什么问题?
继承 Thread
实现 Runnable
使用匿名内部类
callable
线程池创建方式
线程创建方式?
只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作
将一个用户线程设置为守护线程的方式是在 线程对象创建 之前 用线程对象的setDaemon方法
守护线程?
启动当前线程 调用当前线程的 run 方法
start()
线程要执行的操作
run()
释放当前CPU的执行权
yield()
join()
sleep()
判单线程是否存活
isAlive()
让当前线程从运行状态转变成休眠状态,释放锁的资源
wait()
唤醒指定被暂停的线程
notify()
唤醒所有的被暂停的线程
notifyAll()
Thread API
两者都可以暂停线程的执⾏。
两者最主要的区别在于:sleep ⽅法没有释放锁,⽽ wait ⽅法释放了锁 。
Wait 通常被⽤于线程间交互/通信,sleep 通常被⽤于暂停执⾏。
wait() ⽅法被调⽤后,线程不会⾃动苏醒,需要别的线程调⽤同⼀个对象上的 notify() 或者notifyAll() ⽅法。sleep() ⽅法执⾏完成后,线程会⾃动苏醒。或者可以使⽤ wait(long timeout)超时后线程会⾃动苏醒。
说说 sleep() ⽅法和 wait() ⽅法区别和共同点?
new ⼀个 Thread,线程进⼊了新建状态;调⽤ start() ⽅法,会启动⼀个线程并使线程进⼊了就绪状态,当分配到时间⽚后就可以开始运⾏了。 start() 会执⾏线程的相应准备⼯作,然后⾃动执⾏run() ⽅法的内容,这是真正的多线程⼯作。 ⽽直接执⾏ run() ⽅法,会把 run ⽅法当成⼀个 main线程下的普通⽅法去执⾏,并不会在某个线程中执⾏它,所以这并不是多线程⼯作。
为什么我们调⽤ start() ⽅法时会执⾏ run() ⽅法,为什么我们不能直接调⽤run() ⽅法?
同优先级的线程 先进先出队列,先来先服务。
高优先级的线程 使用优先调度的抢占策略。高优先级的线程会抢占低优先级的执行权
setPriority()getPriority()
线程优先级调度?
并发编程的⽬的就是为了能提⾼程序的执⾏效率提⾼程序运⾏速度,但是并发编程并不总是能提⾼程序运⾏速度的,⽽且并发编程可能会遇到很多问题
内存泄漏、上下⽂切换、死锁还有受限于硬件和软件的资源闲置问题
线程任务从保存到再次加载的过程就是一次上下文切换
当前任务在执⾏完 CPU 时间⽚切换到另⼀个任务之前会先保存⾃⼰的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态
什么是上下⽂切换?
多个线程共享一个主内存变量时,在写操作可能受其他线程的影响发生数据冲突
什么是线程安全问题?
1、同步代码块 2、同步方法 3、锁机制Lock
线程安全解决方法?
由于线程被⽆限期地阻塞,因此程序不可能正常终⽌。
互斥条件:该资源任意⼀个时刻只由⼀个线程占⽤。
请求与保持条件:⼀个进程因请求资源⽽阻塞时,对已获得的资源保持不放。
不剥夺条件: 线程已获得的资源在末使⽤完之前不能被其他线程强⾏剥夺,只有⾃⼰使⽤完毕后才释放资源。
循环等待条件: 若⼲进程之间形成⼀种头尾相接的循环等待资源关系
产⽣死锁必须具备以下四个条件
什么是线程死锁?
这个条件我们没有办法破坏,因为我们⽤锁本来就是想让他们互斥的
破坏互斥条件
⼀次性申请所有的资源
破坏请求与保持条件
占⽤部分资源的线程进⼀步申请其他资源时,如果申请不到,可以主动释放它占有的资源
破坏不剥夺条件
靠按序申请资源来预防。按某⼀顺序申请资源,释放资源则反序释放。破坏循环等待条件
破坏循环等待条件
如何避免线程死锁?
synchronized 关键字 主要保证它所修饰的方法或者代码块在同一时间只有一个线程执行
作用于当前 实例对象本身 加锁,进入同步代码前要获得当前对象实例的锁
修饰实例⽅法
作用于当前 类对象本身 加锁,进入同步代码前要获得当前类对象的锁。本类的其他静态方法都要等该方法释放锁
修饰静态⽅法
指定加锁对象(实例对象、类),对给定对象加锁,进入同步代码库前要获得给定对象的锁
修饰代码块
三种使⽤⽅式
在去年的项目钟 短信发送进程中有用到
说说⾃⼰是怎么使⽤ synchronized 关键字,在项⽬中⽤到了吗?
synchronized 关键字的底层原理
如偏向锁、轻量级锁、⾃旋锁、适应性⾃旋锁、锁消除、锁粗化等技术来减少锁操作的开销
锁主要存在四种状态,依次是:⽆锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈⽽逐渐升级。
JDK1.6 之后的synchronized 关键字底层做了哪些优化
synchronized
synchronized 依赖于 JVM 虚拟机层⾯实现的 ,是Java语言的关键字Lock 依赖于 API 是 JDK 层⾯实现,Lock是一个接口。lock() 和 unlock() ⽅法配合try/finally 语句块来完成
synchronized 使用中不需要手动解锁,Lock 需要手动解锁。代码实现 synchronized 要简单
两者都是可重⼊锁
Sync是不可中断的。除非抛出异常或者正常运行完成
在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态。
可实现选择性通知,可以指定唤醒哪个线程,notify()唤醒那个线程由JVM决定可以实现多路通知功能也就是在⼀个Lock对象中可以创建多个Condition实例(即对象监视器)线程对象可以注册在指定的Condition中,从⽽可以有选择性的进⾏线程通知,在调度线程上更加灵活
区别
获取锁
void lock()
如果当前线程未被中断,则获取锁,可以响应中断
void lockInterruptibly()
返回绑定到此 Lock 实例的新 Condition 实例
Condition newCondition()
仅在调用时锁为空闲状态才获取该锁,可以响应中断
boolean tryLock()
如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁
释放锁
void unlock()
Lock API
Lock readLock() //返回用于读取操作的锁 Lock writeLock() //返回用于写入操作的锁
ReadWriteLock 接口只有两个方法:
ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持,而写入锁是独占的。
ReadWriteLock锁
Lock锁 和 synchronized 锁
⾃⼰可以再次获取⾃⼰的内部锁,同⼀个线程每次获取锁,锁的计数器都⾃增1,所以要等到锁的计数器下降为0时才能释放锁比如 在一个同步方法中调用了 另外一个同步锁
可重⼊锁
响应中断的锁
可中断锁
尽可能的按照先后顺序获取锁。synchronized就是非公平锁,Lock可选择
公平锁
主要作⽤就是保证变量的可见性。然后还有⼀个作⽤是防⽌指令重排序
volatile 原理
可以通过标记判断走完run()代码 或者 异常
停止线程方式
创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题
ThreadLocal简介
使用的是类似 Hashmap 的结构
底层
Synchronized是通过线程等待,牺牲时间来解决访问冲突ThreadLocal是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。
ThreadLocal特性
使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。
内存泄漏问题
ThreadLocal
同步 这里讲的同步是指多个线程通过synchronized关键字这种方式来实现线程间的通信。
while轮询的方式
wait/notify机制
管道通信
JAVA多线程中线程之间的通信方式
多线程
通过重复利用已创建的线程减少线程的创建和销毁造成的消耗
降低资源消耗
当任务到达时,可以不用等待线程创建就可以直接执行
提高响应速度
线程时稀缺资源,不断地无限制创建,不仅会消耗系统的资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优、监管
提⾼线程的可管理性
1. 为什么要⽤线程池?
这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
创建一个可缓存的线程池
Executors.newCachedThreadPool()
每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
LinkedBlockingQueue 核心线程和最大线程是相同的
创建固定大小的线程池
Executors.newFixedThreadPool(3)
核心线程和最大都是一个 LinkedBlockingQueue
创建一个单线程化的线程池
Executors.newSingleThreadExecutor()
创建一个定长线程池。此线程池支持定时以及周期性执行任务的需求。——延迟执行
Executors.newScheduledThreadPool(5)
程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
ThreadPoolExecutor的方式
2.线程池的创建
execute()方法没有返回值,所以⽆法判断任务是否被线程池执⾏成功与否
submit()有返回值,线程池会返回⼀个 Future 类型的对象,通过这个 Future 对象可以判断任务是否执⾏成功
3.执⾏execute()⽅法和submit()⽅法的区别是什么呢?
singleThreadPool.shutdownNow();//对正在执行的任务停止singleThreadPool.shutdown();//只是不接受新任务
4.如何终止线程池?
线程池中核心线程数的最大值
corePoolSize
线程池中能拥有最多线程数
maximumPoolSize
(1)如果没有空闲的线程执行该任务且当前运行的线程数少于corePoolSize,则添加新的线程执行该任务。 (2)如果没有空闲的线程执行该任务且当前的线程数等于corePoolSize同时阻塞队列未满,则将任务入队列,而不添加新的线程。 (3)如果没有空闲的线程执行该任务且阻塞队列已满同时池中的线程数小于maximumPoolSize,则创建新的线程执行任务。 (4)如果没有空闲的线程执行该任务且阻塞队列已满同时池中的线程数等于maximumPoolSize,则根据构造函数中的handler指定的策略来拒绝新的任务。
用于缓存任务的阻塞队列
workQueue
表示空闲线程的存活时间
keepAliveTime
表示keepAliveTime的单位
TimeUnitunit
handler
threadFactory
5.ThreadPoolExecutor
线程池
并发编程
MySQL 开源 轻量级 免费
Oracle 非开源 重量级 收费
MySQL对于事务默认是不支持的,innodb可以支持
Oracle对于事务是完全支持
事务
MySQL以表级锁为主,对资源锁定的粒度很大,如果一个session对一个表加锁时间过长,会让其他session无法更新此表中的数据。 虽然InnoDB引擎的表可以用行级锁,但这个行级锁的机制依赖于表的索引,如果表没有索引,或者sql语句没有使用索引,那么仍然使用表级锁。 Oracle使用行级锁,对资源锁定的粒度要小很多,只是锁定sql需要的资源,并且加锁是在数据库中的数据行上,不依赖与索引。所以Oracle对并 发性的支持要好很多。
并发性
MySQL使用 limit分页公式 Oracle 使用 伪列ROWNUM
分页方式不同
字符串 单双引号不同
MySQL 可以设置自增,Oracle使用序列
主键
时间
操作
MySQL和Oracle的区别
MyISAM和InnoDB区别
原子性(Atomicity): 事务是最⼩的执⾏单位,不允许分割。事务的原⼦性确保动作要么全部 完成,要么完全不起作⽤;
一致性(Consistency):执行事务前后,数据保持⼀致,多个事务对同一个数据读取的结果是 相同的;
隔离性(Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的
持久性(Durability):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据 库发⽣故障也不应该对其有任何影响。
事物的四⼤特性(ACID)
前一个修改了还没提交事务,第二个事务访问这个数据,并使用了它。
脏读(没有取到最新数据)
在一个事务读取⼀个数据时,另外一个事务也访问了该数据, 那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。第一个事务内的修改结果就被丢失,因此称为丢失修改
丢失修改(第二个覆盖了第一个)
指在一个事务内多次读同一数据。在这个事务还没有结束 时,另一个事务也访问该数据。那么在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读
不可重复读(两次读取不一样)
幻读与不可重复读类似。它发生在一个事务(T1)读取了多次数据,接 着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
幻读(读取到下一个事务的信息)
并发事务带来哪些问题
最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
READ-UNCOMMITTED(读取未提交)
允许读取并发事务已经提交的数据,可以阻⽌脏读,但是幻读或不可重复读仍有可能发⽣。
READ-COMMITTED(读取已提交)
对同⼀字段的多次读取结果都是⼀致的,除⾮数据是被本身事务⾃⼰所修改,可以阻⽌脏读和不可重复读,但幻读仍有可能发⽣。
REPEATABLE-READ(可重复读)
最⾼的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执⾏,这样事务之间就完全不可能产⽣⼲扰,也就是说,该级别可以防⽌脏读、不可重复读以及幻读
SERIALIZABLE(可串⾏化)
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。
InnoDB 存储引擎默认使⽤ REPEAaTABLE-READ(可重读) 并不会有任何性能损失。 InnoDB 存储引擎在 REPEATABLE-READ(可重读) 事务隔离级别下使⽤的是Next-Key Lock 锁算法,因此可以避免幻读的产⽣,
InnoDB 存储引擎在 分布式事务 的情况下⼀般会⽤到 SERIALIZABLE(可串⾏化) 隔离级别。
事务隔离级别有哪些?MySQL的默认隔离级别是?
当MySQL单表记录数过⼤时,数据库的CRUD性能会明显下降,会使用优化。
务必禁⽌不带任何限制数据范围条件的查询语句。⽐如:我们当⽤户在查询订单历史的时候,我们可以控制在⼀个⽉的范围内;
限定数据的范围(按时间分表)
经典的数据库拆分⽅案,主库负责写,从库负责读;
读/写分离(没用过)
把⼀张列⽐较多的表拆分为多张表
垂直分区(拆表)
相同的表放在不同的数据库中去
⽔平分区(分库)
⼤表优化
在连接池中,创建连接后,将其放置在池中,并再次使⽤它,因此不必建⽴新的连接。如果使⽤了所有连接,则会建⽴⼀个新连接并将其添加到池中。
连接池还减少了⽤户必须等待建⽴与数据库的连接的时间。
解释⼀下什么是池化设计思想。什么是数据库连接池?为什么需要数据库连接池?
Mysql 当时用的是 主键自增的方式 用过的方法时有一张表专门用来记录自增主键的id。每次查询出最大值,然后自增1000缓存起来,然后每次新增是用到的就直接取。用完在取,参数都是可配置的。
Oracle 系统时间 + 随机数
要执行的SQL语句,刚好这条语句涉及到的表,别人在用,并且加锁了,我们拿不到锁,只能慢慢等待别人释放锁了。或者,表没有加锁,但要使用到的某一行被加锁了,这也会造成上述问题。
1、大多数情况是正常的,只是偶尔会出现很慢的情况。
没有加索引 或 索引没有生效
2、在数据量不变的情况下,这条SQL语句一直以来都执行的很慢。
避免全表扫描应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描应尽量避免在 where 子句中对字段进行 null 值判断比如子查询的效率没有关联查询快,in 可以换左右链接组合的方式。
3.SQL语句需要优化
一条SQL语句执行得很慢的原因有哪些?
on条件是在生成临时表时使用的条件
where条件是在临时表生成好后,再对临时表进行过滤的条件。
sql 的 on 和 where
MySQL主键应当是对用户没有意义的。MySQL主键应该是单列的,以便提高连接和筛选操作的效率永远也不要更新MySQL主键MySQL主键不应包含动态变化的数据,如时间戳、创建时间列、修改时间列等MySQL主键应当有计算机自动生成。
设计原则
数据库自动编号,速度快,而且是增量增长,聚集型主键按顺序存放,对于检索非常有利。
数字型,占用空间小,易排序,在程序中传递方便。
不支持水平分片架构,水平分片的设计当中,这种方法显然不能保证全局唯一。
子主题
缺点
自增ID
MySQL主键设计
MySQL
避免全表扫描,在where 和 order by 涉及的列上面建索引
where 子句中对字段进行 null 值判断(col is null)。使用默认值代替null
where 子句中使用!=或<>操作符
where 子句中使用 or 来连接条件
like以%开头
where 子句中对字段进行表达式操作
where子句中对字段进行函数操作
复合索引没有按照左从原则
如果mysql觉得全表扫描更快时(数据少)
避免索引失效,否则将导致引擎放弃使用索引而进行全表扫描
in() 适合B表比A表数据小的情况
exists() 适合B表比A表数据大的情况
exists 和 in 使用原则
违反创建索引原则
sql优化
1.什么是索引?
(1)可以大大加快数据的检索速度,这也是创建索引的最主要的原因。(2)通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
索引的优点
(1)时间方面:创建索引和维护索引要耗费时间,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,会降低增/改/删的执行效率,因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。(2)空间方面:建立索引会占用磁盘空间的索引文件。
索引的缺点
2、索引有哪些优缺点?
Where 有索引的字段作为条件,可以提高查询效率。Order by 如果字段已经建立索引,则索引本身就是有序的,因此直接按照索引的顺序和映射关系逐条取出数据即可。Join on 对join语句匹配关系(on)涉及的字段建立索引能够提高效率
3、索引使用场景
数据列不允许重复,不允许为NULL,一个表只能有一个主键。
主键索引
数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引
唯一索引
基本的索引类型,没有唯一性的限制,允许为NULL值
普通索引
ALTER TABLE table_name ADD FULLTEXT (column); -- 创建全文索引
是目前搜索引擎使用的一种关键技术
全文索引
4、索引有哪几种类型?
5、索引的数据结构
索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。
索引的原理很简单,就是把无序的数据变成有序的查询(1)把创建了索引的列的内容进行排序(2)对排序结果生成倒排表(3)在倒排表内容上拼上数据地址链(4)在查询的时候,**先拿到倒排表内容,再取出数据地址链**,从而拿到具体数据
6、索引的基本原理
7.Hash索引和B+树所有有什么区别或者说优劣呢?
(1)适合索引的列是出现在where子句中的列,或者连接子句中指定的列(2)基数较小的类,索引效果较差,没有必要在此列建立索引(3)使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间(4)不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。
8、索引设计的原则?
9、创建索引的原则(重中之重)
应该指定列为NOT NULL,除非你想存储NULL。在mysql中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值
非空字段
变量各个取值之间的差异程度)的列放到联合索引的前面,可以通过count()函数查看字段的差异值,返回值越大说明字段的唯一值越多字段的离散程度高;
取值离散大的字段
数据库的数据存储以页为单位一页存储的数据越多一次IO操作获取的数据越大效率越高
索引字段越小越好
10、创建索引时需要注意什么?
索引
Redis 是非关系型数据库,与传统的数据库不统在于它存在内存中
简介
⽤户第⼀次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该⽤户访问的数据存在缓存中,这样下⼀次再访问这些数据的时候就可以直接从缓存中获取了
⾼性能:
直接操作缓存能够承受的请求是远远⼤于直接访问数据库的
⾼并发:
作用
redis 的线程模型 单线程的模型
redis⽀持更丰富的数据类型(⽀持更复杂的应⽤场景)
集群模式:memcached没有原⽣的集群模式,需要依靠客户端来实现往集群中分⽚写⼊数据;但是 redis ⽬前是原⽣⽀持 cluster 模式的.
Memcached是多线程,⾮阻塞IO复⽤的⽹络模型;Redis使⽤单线程的多路 IO 复⽤模型。
redis 和 memcached 的区别
的key-value类型 常规计数:微博数,粉丝数等。
sh 是⼀个 string 类型的 field 和 value 的映射表 hash 特别适合⽤于存储对象
Hash
list 就是链表 Redis最重要的数据结构之⼀,⽐如微博的关注列表,粉丝列表,消息列表等功能都可以⽤Redis的 list 结构来实现
set 对外提供的功能与list类似是⼀个列表的功能,特殊之处在于 set 是可以⾃动排重的
和set相⽐,sorted set增加了⼀个权重参数score,使得集合中的元素能够按score进⾏有序排列在直播系统中,实时排⾏信息包含直播间在线⽤户列表,各种礼物排⾏榜,弹幕消息
Sorted Set
redis 常⻅数据结构以及使⽤场景分析
准备一个分布式锁,大家去抢锁,抢到锁就做set操作
分布式锁
利用消息队列
如何解决 Redis 的并发竞争 Key 问题
Redis
因为在一个商城中商品的信息数据会很多,使用模糊查询方式 因为前置规则会放弃索引,查询效率会非常低。而ES可以实现全文检索索引,提高查询的效率。将常用的数据字段放在ES中可以很快的查询到结果。
为什么要使用Elasticsearch?
快速存储、搜索、分析数据
Elasticsearch是如何实现Master选举的?
ElasticSearch
Mongodb
数据库
spring是一个轻量级的框架,能简化企业级开发,减少开发过程的代码量,也能很好的整合其它框架。spring的核心是IOC和AOP,IOC说白了就是提供对象的一个容器,DI依赖注入就是属性赋值,AOP就是根据动态代理实现的业务流程以外的扩展功能(事务处理、日志管理、权限控制),分为切面、切入点和通知点。spring还提供了Jdbc框架的轻量级封装,还提供声明是事务,还跟据MVC结构开发了Spring MVC框架。
对spring框架的理解
Spring Core : 基础模块 提供IOC容器 实现对Bean的管理
Spring AOP :提供了⾯向切⾯的编程实现。
Spring Web : 为创建Web应⽤程序提供⽀持
Spring JDBC : Java数据库连接。
Spring Aspects : 该模块为与AspectJ的集成提供⽀持。
Spring JMS :Java消息服务。
pring ORM : ⽤于⽀持Hibernate等ORM⼯具。
spring七大模块
IOC的理解
IoC(Inverse of Control:控制反转)是⼀种设计思想,就是 将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
描述
它生成的bean完全由自己管理,生命周期可以自己控制传统的方式没有任何容器去管理它的生命周期,完全是靠回收机制回收
第一,资源集中管理,实现资源的可配置和易管理。第二,降低了使用资源双方的依赖程度。
好处
工厂模式 反射 xml解析
技术
首先得获得类的全路径,该类还得有构造器,如果你要是写了有参构造器的话,必须得显示写无参构造器。默认情况下,该类是自带无参构造器。
反射机制
根据唯一标示 获取该实例
容器特点
xml配置方式 注解的方式
容器实现方式
IOC初始化流程?
DI依赖注入流程?
指的是构建实例的控制权
控制指的是什么?
控制权由其他类(引用类)转为spring了
反转指的是什么?
容器底层的数据结构
是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;IOC容器的基本实现,一般不会直接使用。
BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;
BeanFactory
1) 国际化(MessageSource)2) 访问资源,如URL和文件(ResourceLoader)3) 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层 4) 消息发送、响应机制(ApplicationEventPublisher)5) AOP(拦截器)
应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;开发使用
ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;
ApplicationContext
ClassPathXmlApplicationContext:可以加载类路径下的配置文件,要求配置文件必须在类路径下面
FileSystemXmlApplicationContext:可以加载磁盘任意路径下的配置文件
AnnotationConfigApplicationContext:用于读取注解创建容器
三个常用的实现类
BeanFactory和ApplicationContext的区别
Spring IOC原理
AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
AOP是什么?
日志记录
权限验证
事务管理(spring 的事务就是用AOP实现的)
效率检查(个人在代码上,喜欢用注解+切面,实现校验,redis分布式锁等功能)
AOP的应用场景有哪些呢?
Spring AOP就是基于动态代理实现的;如果要代理的对象实现了某个接⼝,那么Spring AOP会使⽤JDK Proxy去创建代理对象;⽽对于没有实现接⼝的对象就⽆法使⽤ JDK Proxy 去进⾏代理了,这时候Spring AOP会使⽤ Cglib ⽣成⼀个被代理对象的⼦类来作为代理.
springAop的底层是怎样实现的?
有接口 使用 JDK 代理 创建接口实现类的代理增强类的方法
没有接口 使用 CGlib代理 创建一个被代理的子类来做代理(重写增强子类方法)
运行期,生成字节码,再加载到虚拟机中,JDK是利用反射原理,CGLIB使用了ASM原理。
是编译时期进行织入,还是运行期进行织入?
初始化的时候,已经将目标对象进行代理,放入到spring 容器中
初始化时期织入还是获取对象时织入?
要看条件,如果实现了接口的类,是使用jdk。如果没实现接口,就使用cglib。
spring AOP 默认使用jdk动态代理还是cglib?
AOP底层是动态代理
哪些方法可以被增强,这些方法就是连接点
连接点
实际被增强的方法就是切入点
切入点
通知(增强)
把增强动作应用到切入点的过程
切面
AOP的基本概念?
前置通知:在我们执行目标方法之前运行(@Before)
异常通知:在我们的目标方法出现异常后运行(@AfterThrowing)
最终通知:在我们的目标方法正常返回值后运行(@AfterReturning)
通知(增强)实现
@Aspect 定义切面类
@Component 加载到IOC容器
execution([权限修饰符][返回类型][全路径][方法名称][参数])@Before(\"execution(* com.nine.spring.springaop.IUserDao.addUser(..))\")
定义切入点函数
@Pointcut注解进行定义切入点函数 表达式只用一次 相同接入点
启动@aspectj的自动代理支持 <aop:aspectj-autoproxy /> spring boot @EnableAspectJAutoProxy
增强类 基于注解的方式
AspectJ 不属于 spring框架,独立的AOP框架。 一般在spring中两者同时使用实现AOP操作。
Spring AOP 属于运⾏时增强,⽽ AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),⽽AspectJ 基于字节码操作(Bytecode Manipulation)。Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java ⽣态系统中最完整的 AOP 框架了。
AspectJ 相⽐于 Spring AOP 功能更加强⼤,但是 Spring AOP 相对来说更简单,如果我们的切⾯比较少,那么两者性能差异不⼤。但是,当切⾯太多的话,最好选择 AspectJ ,它⽐Spring AOP 快很多。
spring AOP 和 AspectJ的关系?
Spring AOP原理
第一种方式:使用默认构造函数创建
第二种方式:使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)创建jar包中的类时可使用此方法
第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
创建Bean的三种方式
singleton:单例模式(默认的)
prototype:多例模式
request:作用于web应用的请求范围
session:作用于web应用的会话范围
global session:作用于集群环境的会话范围,当不是集群的时候就是session
Spring 中的 bean 的作⽤域有哪些? scope属性取值
<!-- bean 的生命周期 单例对象 出生:当容器创建时对象出生 活着:只要容器还在对象就活着 死亡:单例对象的生命周期和容器的生命周期相同 多利对象 出生:当我们使用对象是 spring框架为我们创建对象 活着:只要对象还在使用过程中就还活着 死亡:对象长时间不用,且没有别的对象引用的时候,由Java的垃圾回收器回收。 -->
bean 的生命周期
第一种使用:构造方法 第二种使用:set方法 第三种使用:注解的方式
注入的方式有3种
容器bean管理
@Component:相当于把对象存在容器中 (没有明确对象)
@Service:在业务逻辑层使用
@Repository:在数据访问层使用
@Controller:在展现层使用,控制器的声明
将⼀个类声明为Spring的 bean 的注解有哪些?
自动按照类型注入,只要容器中有唯一的一个bean类型和要注入的变量匹配,就可以注入成功。 如果ioc容器中没有任何bean类型和变量匹配,则注入失败。 如果IOC容器中有多个bean匹配时,根据先按照类型 再 使用变量名称去匹配
@Autowired
做用:在按照类中注入的基础之上 在同过名称注入。它在给类成员注入时不能单独使用。但是在给方法注入时可以Qualifier 组合 Autowired 使用
@Qualifier
直接按照bean的Id注入 可以单独使用
@Resource
属性:value用于指定数据的值 它可以使用spring的SpEl (就是Spring的el表达式)
@value
用于注入数据时
在spring ioc的过程中,优先解析@Component,@Service,@Controller注解的类。其次解析配置类,也就是@Configuration标注的类。最后开始解析配置类中定义的bean。
Spring注解实现原理
⼤部分时候我们并没有在系统中使⽤多线程,所以很少有⼈会关注这个问题。单例 bean 存在线程问题,主要是因为当多个线程操作同⼀个对象的时候,对这个对象的⾮静态成员变量的写操作会存在线程安全问题。
Spring 中的单例 bean 的线程安全问题了解吗?
Bean创建的时候可以给Bean打标,如果递归调用回来发现正在创建种,说明循环依赖了
怎么检测是否循环依赖?
spring怎么解决循环依赖问题?
AOP功能的原理就是使用了代理模式的思想(1.JDK动态代理,2.CGLib 字节码生成技术代理)
提供了全局的访问点Bean Factory
spring的beanFactory就是简单的工厂模式,根据传入唯一标识获得bean对象
依赖注入需要使用BeanWrapper
装饰器模式
spring 的监听机制 需要继承ApplicationEvent
Bean实例化的时候决定采用哪种方式实现Bean实例(反射或者CGLib动态字节码实现)
策略模式
spring种用了哪些设计模式?
JDK代理模式
动态代理和静态代理的区别
CGLib和JDK代理的区别
Spring声明式事务配置
Spring
Spring MVC
Spring Security所解决的问题就是安全访问控制,而安全访问控制功能其实就是对所有进入系统的请求进行拦截,校验每个请求是否能够访问它所期望的资源。可以通(FiltkAgP等技术来实现,Spring Security对Web资源的保护是靠Filter实现的,所以从这kFilter入手,逐步深入Spring Security原理。当初始化Spring Security时,会创建一个名为springSecurityFilterchain的Servlet过滤器,类型为argspringframework.security.web.FilterChainProxy,它实现了javax.serlet.Filter,因此外部的请求会经过此类
UsernamePasswordAuthenticationFilter
SpringSecurity
设计目的是简化Spring应用的搭建和开发过程,为Spring开发提供更加简单的使用和快速开发的技巧
大量的自动配置,不需要编写太多的xml配置文件
内置tomcat服务器,不需要配置tomcat容器就可以直接运行
具有功能更加强大的服务体系,包括嵌入式服务、安全、性能指标、健康检口具有功能更加强大的服务体系,包括嵌入式服务、安全、性能指标、健康检查等
可以很好的兼容第三方插件,只要引入相关依赖就可以实现
为什么用springboot?
@Configuration
条件注入
@ConditionalOnBean
spring boot
启动类
@SpringBootApplication
@EnableEurekaServer
spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可
Restful风格
Springboot
整体解决方案和框架成熟 社区热度高 可维护性 学习曲线
为什么选择springcloud做微服务架构?
Spring Cloud 是一套完整的微服务解决方案,它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud特别适合中小型互联网公司开发自己的分布式系统基础设施,从容应对业务发展,大大减少开发成本
分布式微服务下的一站式解决方案,是各个微服务技术落地的技术集合,微服务全家桶。
Spring Cloud对微服务基础框架Netflix的多个开源组件进行了封装,同时又实现了和云端平台以及和Spring Boot开发框架的集成。
Spring Cloud为微服务架构开发涉及的配置管理,服务治理,熔断机制,智能路由,微代理,控制总线,一次性token,全局一致性锁,leader选举,分布式session,集群状态管理等操作提供了一种简单的开发方式。
Spring Cloud 为开发者提供了快速构建分布式系统的工具,开发者可以快速的启动服务或构建应用、同时能够快速和云平台资源进行对接。
什么是springcloud?
为各个微服务之间提供:配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话
springcloud做用?
springboot专注于单个个体微服务的快速开发(微观);
springboot 可以离开 springcloud单独开发应用,但是springcloud 不能离开pringboot springcloud 是很多组件,都是依赖SpringBoot启动的 这么理解
springcloud和springboot的关系?
服务注册于发现
Eureka
实现负载均衡,从一个服务的多台机器中选择一台
Ribbon
基于动态代理机制,根据注解和选择的机器,拼接请求 url 地址,发起请求。
Feign
提供线程池,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。
Hystrix
网关管理,由 Zuul 网关转发请求给对应的服务。
zuul
springcloud核心组件?
最大的区别:spring cloud抛弃了dubbo的RPC通信,使用HTTP的REST方式REST比RPC更加灵活,服务提供方和服务调用方约定协议,不需要代码级别的强依赖,这在强调快速演化的微服务下,显得更加合适
springcloud和dobbo对比
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
任何一个服务都不能直接去掉用,都需要通过注册中心来调用。通过服务中心来获取服务你不需要关注你调用的项目IP地址,由几台服务器组成,每次直接去服务中心获取可以使用的服务去调用既可。
各个微服务启动时,会通过 Eureka Client 向 Eureka Server 注册自己,Eureka Server 会存储该服务的信息也就是说,每个微服务的客户端和服务端,都会注册到 Eureka Server,这就衍生出了微服务相互识别的话题同步:每个 Eureka Server 同时也是 Eureka Client(逻辑上的) 多个 Eureka Server 之间通过复制的方式完成服务注册表的同步,形成 Eureka 的高可用识别:Eureka Client 会缓存 Eureka Server 中的信息 即使所有 Eureka Server 节点都宕掉,服务消费者仍可使用缓存中的信息找到服务提供者(笔者已亲测)续约:微服务会周期性(默认30s)地向 Eureka Server 发送心跳以Renew(续约)信息(类似于heartbeat)续期:Eureka Server 会定期(默认60s)执行一次失效服务检测功能 它会检查超过一定时间(默认90s)没有Renew的微服务,发现则会注销该微服务节点
Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。Eureka Server本身也是一个服务,默认情况下会自动注册到Eureka注册中心。
服务提供者启动时,会通过 Eureka Client 向 Eureka Server 注册信息,Eureka Server 会存储该服务的信息,Eureka Server 内部有二层缓存机制来维护整个注册表
服务注册
服务消费者在调用服务时,如果 Eureka Client 没有缓存注册表的话,会从 Eureka Server 获取最新的注册表
提供注册表
Eureka Client 通过注册、心跳机制和 Eureka Server 同步当前客户端的状态。
同步状态
Eureka Server
Eureka Client 是一个 Java 客户端,用于简化与 Eureka Server 的交互。Eureka Client 会拉取、更新和缓存 Eureka Server 中的信息。因此当所有的 Eureka Server 节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者,但是当服务有更改的时候会出现信息不一致。
Eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持
Eureka Client
创建 Eureka Server
spring-cloud-starter-eureka
spring-boot-starter-actuator
注册 Eureka Client 在服务提供端
搭建流程
当Eureka Server 节点在短时间内丢失了过多实例的连接时(比如网络故障或频繁启动关闭客户端)节点会进入自我保护模式,保护注册信息,不再删除注册数据,故障恢复时,自动退出自我保护模式。
Eureka 自我保护机制
Eureke 集群搭建
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
CAP原则
作为注册中心 Eureka 和 zookeeper区别
Spring Cloud Eureka
spring-cloud-starter-eureka-server
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现
通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用
Ribbon 是什么?
LB 负债均衡(load balance):将用户的请求均摊分配到多个服务上 ,从而达到系统的高可用。旨在优化资源使用,最大吞吐量,最小响应时间并避免任何单一资源的过载
dubbo 和 springcloud 都提供了负载均衡 spring cloud负载均衡算法可以自定义
集中式 LB
进程式
负载均衡的分类
Ribbon 能干吗?
负载均衡器Ribbon中的IRule负责选择什么样的负载均衡算法
RoundRobinRule:轮询(默认)
RandomRule:随机
RetryRule:重试(先按照轮询规则获取服务,如果获取服务失败则在指定时间内进行重试)
AvailabilityFilteringRule:会先过滤掉由于多次访问故障而处于断路器状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问;
WeightedResponseTimeRule:根据平均响应时间计算所有服务的权重,响应时间越快的服务权重越大被选中的概率越大。刚启动时如果统计信息不足,则使用RoundRobinRule(轮询)策略,等统计信息足够,会切换到WeightedResponseTimeRule;
BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务;
ZoneAvoidanceRule:复合判断Server所在区域的性能和Server的可用性选择服务器;
Ribbon算法
@Bean //修改轮询规则为随机 public IRule iRule(){ return new RandomRule(); }
在配置类中注入相应的Bean
编写自定义的轮写规则类 继承 AbstractLoadBalancerRule
编写配置类将自定义的规则类注入到IOC容器中
编写自定义的轮询规则
请注意,自定义的必须是@Configuration,它不在主应用程序上下文的@ComponentScan中,否则将由所有@RibbonClients共享。 如果您使用@ComponentScan(或@SpringBootApplication),则需要采取措施避免包含(例如将其放在一个单独的,不重叠的包中,或者指定要在@ComponentScan)。
Ribbon中的IRule自定义原则
自定义算法
Ribbon中的IRule
Spring Cloud Ribbon
Feign是一个封装了http请求的轻量级框架,通过接口注解的方式实现HTTP调用(面向接口的编程方式)Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。
Feign是什么?
封装了Http调用流程,更适合面向接口化的变成习惯
Feign做什么?
Feign是如何设计的?
两个都是实现软负载均衡的组件。Ribbon和Feign都是用于调用其它服务
Feign 是在 Ribbon 的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端。采用接口的方式, 只需要创建一个接口,然后在上面添加注解即可 ,将需要调用的其他服务的方法定义成抽象方法即可, 不需要自己构建 http 请求。然后就像是调用自身工程的方法调用,而感觉不到是调用远程方法,使得编写 客户端变得非常容易不过要注意的是抽象方法的注解、方法签名要和提供服务的方法完全一致
Ribbon 是一个基于 HTTP 和 TCP 客户端 的负载均衡的工具。它可以 在客户端 配置 RibbonServerList(服务端列表),使用 HttpClient 或 RestTemplate 模拟 http 请求
feign和ribbon区别?
Spring Cloud Feign
简单的来说就是由于服务提供者A不可用,导致服务调用者B对A的请求阻塞,没有相关的机制通知或解决请求阻塞,导致在服务调用者B对A请求的阻塞越来越多,阻塞请求变多并且不断对A进行请求重试导致服务调用者B所在的系统的资源会被耗尽,而服务调用者B所在的系统可能并不会只有对A的调用,还有存在对其他服务提供者的调用,因为调用A把系统资源已经耗尽了,导致也无法处理对非A请求,而且这种不可用可能沿请求调用链向上传递,比如说服务调用者C会调用B的服务,因为B所在的系统不可用,导致C也不可用,这样级联导致阻塞请求越来越多,表现为多个系统都不可用了,这种现象被称为\"雪崩效应\"。
程序bug导致服务不可用,或者运行缓慢
缓存击穿,导致调用全部访问某服务,导致down掉
访问量的突然激增。
硬件问题,这感觉只能说是点背了
常见的导致雪崩的情况有以下几种
横向扩充
限流
熔断
解决方案
雪崩效应
在分布式环境中,不可避免地,许多服务依赖关系中的一些会失败。hystrix是一个库,它通过增加延迟容差和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止跨服务的级联故障以及提供回退选项来实现这一点,所有这些都提高了系统的整体弹性。
在分布式系统中,每个服务都可能会调用很多其他服务,被调用的那些服务就是依赖服务,有的时候某些依赖服务出现故障也是很常见的。 Hystrix 可以让我们在分布式系统中对服务间的调用进行控制,加入一些调用延迟或者依赖故障的容错机制。Hystrix 通过将依赖服务进行资源隔离,进而阻止某个依赖服务出现故障时在整个系统所有的依赖服务调用中进行蔓延;同时Hystrix 还提供故障时的 fallback 降级机制。 总而言之,Hystrix 通过这些方法帮助我们提升分布式系统的可用性和稳定性。
什么是Hystrix?
通过第三方客户端库提供对延迟和依赖访问(通常通过网络)故障的保护和控制
停止复杂分布式系统中的级联故障
快速失败并迅速恢复
在可能的情况下,后退并优雅地降级。
实现近实时监控,警报和操作控制。
Hystrix什么作用?
服务熔断 在服务端实现
启动类:@EnableCircuitBreaker 添加对熔断器的配置
服务异常的方法可以注释:@HystrixCommand 然后指定处理方式
当某个异常条件被触发就直接熔断整个服务,而不是一直等到此服务超时
服务熔断
1.implements FallbackFactory 写一个服务处理工厂
2.@FeignClient(value = \"SPRINGCLOUD-PROVIDER-DEPT\
服务降级
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> <version>1.4.6.RELEASE</version> </dependency>
创建监控页面:
将服务加入监控中:@EnableCircuitBreaker //添加对熔断器的配置在服务端:添加一个servlet bean
Hystrix监控
服务限流
Hystrix能干嘛?
当一个服务调用另一个服务由于网络原因或自身原因出现问题,调用者就会等待被调用者的响应 当更多的服务请求到这些资源导致更多的请求等待,发生连锁效应(雪崩效应)断路器有完全打开状态:一段时间内 达到一定的次数无法调用 并且多次监测没有恢复的迹象 断路器完全打开 那么下次请求就不会请求到该服务半开:短时间内 有恢复迹象 断路器会将部分请求发给该服务,正常调用时 断路器关闭关闭:当服务一直处于正常状态 能正常调用
springcloud断路器作用?
Spring Cloud Hystrix
zuul旨在实现动态路由、监控、弹性和安全性。它还能够根据需要将请求路由到多个amazon自动伸缩组。
路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础。Zuul和Eureka进行整合, 将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。
什么是zuul
Spring Cloud Zuul
在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client。
1、添加pom依赖2、配置文件添加相关配置3、启动类添加注解@EnableConfigServer
bootstrap.yml(bootstrap.properties)用来在程序引导时执行,应用于更加早期配置信息读取,如可以使用来配置application.yml中使用到参数等application.yml(application.properties) 应用程序特有配置信息,可以用来配置后续各个模块中需使用的公共参数等。bootstrap.yml 先于 application.yml 加载
当使用 Spring Cloud Config Server 的时候,你应该在 bootstrap.yml 里面指定 spring.application.name 和 spring.cloud.config.server.git.uri和一些加密/解密的信息
bootstrap.yml和application.yml
什么是SpringCloudConfig?
spring cloud bus(消息总线) 将分布式的节点用轻量的消息代理连接起来,它可以用于广播配置文件的更改或者服务直接的通讯,也可用于监控。如果修改了配置文件,发送一次请求,所有的客户端便会重新读取配置文件。
1.添加依赖,2.配置rabbimq
什么是Spring Cloud Bus?
SpringCloud
${} 是 Properties ⽂件中的变量占位符,它可以⽤于标签属性值和 sql 内部,属于静态⽂本替换,⽐如${driver}会被静态替换为 com.mysql.jdbc.Driver 。
#{} 是 sql 的参数占位符,Mybatis 会将 sql 中的 #{} 替换为?号 #{item.name}
#{}和${}的区别是什么?
查询的sql语句中定义字段名的别名
通过<resultMap>来映射字段名和实体类属性名的一一对应的关系
当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
在Java代码中添加sql通配符
在sql语句中拼接通配符,会引起sql注入
模糊查询like语句该怎么写?
Dao接口的名称: 就是对应的Mapper.xml中namespace的值
Dao接口的方法名:就是映射文件中Mapper的Statement的id值
Dao接口方法内的参数就是传递给sql的参数
Dao接口中方法是不能重载的,因为是全限名+方法名的保存和寻找策略。
Mapper 接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象proxy,代理对象会拦截接口方法,转而执行MapperStatement所代表的sql,然后将sql执行结果返回。
Dao 接⼝的⼯作原理是什么?Dao 接⼝⾥的⽅法,参数不同时,⽅法能重载吗?
可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。 分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
Mybatis - 通用分页拦截器 PageHelper项目地址 : http://git.oschina.net/free/Mybatis_PageHelper
Mybatis是如何进行分页的?分页插件的原理是什么?
第一种是使用<resultMap>标签,逐一定义数据库列名和对象属性名之间的映射关系。第二种是使用sql列的别名功能,将列的别名书写为对象属性名。
Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
设置SqlSession为批量操作类型
通过foreach 遍历需要操作的值
如何执行批量插入?
传入的对象中可以取到
如何获取自动生成的(主)键值?
直接接口方法中传入参数注解@param封装Map集合
在mapper中如何传递多个参数?
Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断并动态拼接sql的功能。Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。
Mybatis动态sql有什么用?执行原理?有哪些动态sql?
还有很多其他的标签, <resultMap> 、 <parameterMap> 、 <sql> 、 <include> 、 <selectKey> ,加上动态 sql的 9 个标签, trim|where|set|foreach|if|choose|when|otherwise|bind 等,其中为 sql⽚段标签,通过 <include> 标签引⼊ sql ⽚段, <selectKey> 为不⽀持⾃增的主键⽣成策略标签。
Xml 映射⽂件中,除了常⻅的 select|insert|updae|delete 标签之外,还有哪些标签?
MyBatis实现一对一有几种方式?具体怎么操作的?
association指的就是一对一collection指的就是一对多查询
Mybatis仅支持association关联对象和collection关联集合对象的延迟加载
它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis的一级、二级缓存
什么是MyBatis的接口绑定?有哪些实现方式?
① Mapper接口方法名和mapper.xml中定义的每个sql的id相同;② Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同;③ Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;④ Mapper.xml文件中的namespace即是mapper接口的类路径。
使用MyBatis的mapper接口调用时有哪些要求?
Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。编写插件:实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
简述Mybatis的插件运行原理,以及如何编写一个插件。
mapper xml文件
生成实体类 实体Example
生成mapper接口
生成sql映射文件
Mybatis自动生成插件 generatorConfiguration
sql中的通配符 替换的不是变量,而是一段sql片段,导致sql结构变化。
sql注入
Mybatis
开源框架
其实MVC架构就是一个单体架构。
代表技术:Struts2、springMVC、Spring、Mybatis 等等。
MVC架构
RPC(Remote Procedure Call)远程过程调用,他是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议
代表技术:Thrift、Hessian等等。
RPC架构
SOA(Service Oriented Architecture)面向服务架构。
代表技术:Mule、WSO2
SOA是基于分布式架构演变过来的,SOA架构代表面向服务架构,面向与业务逻辑层。将共同的业务代码抽取出来服务化,给其他的接口调用。服务与服务之间调用 用的是RPC远程调用技术
SOA实现方式:底层基于SOAP和ESB(数据总线)实现的
ESB(企业服务总线),简单 来说 ESB 就是一根管道,用来连接各个服务节点。为了集 成不同系统,不同协议的服务,ESB 做了消息的转化解释和路由工作,让不同的服务互联互通。
SOAP 是一种简单的基于 XML 的协议,它使应用程序通过 HTTP 来交换信息。
站在系统的角度,解决企业系统间的通信问 题,把原先散乱、无规划的系统间的网状结构,梳理成 规整、可治理的系统间星形结构,这一步往往需要引入 一些产品,比如 ESB、以及技术规范、服务管理规范; 这一步解决的核心问题是【有序】
系统集成
站在功能的角度,把业务逻辑抽象成 可复用、可组装的服务,通过服务的编排实现业务的 快速再生,目的:把原先固有的业务功能转变为通用 的业务服务,实现业务逻辑的快速复用;这一步解决 的核心问题是【复用】
系统的服务化
站在企业的角度,把企业职能抽象成 可复用、可组装的服务;把原先职能化的企业架构转变为服务化的企业架构,进一步提升企业的对外服务能力;“前面两步都是从技术层面来解决系统调用、系统功能复用的问题”。第三步,则是以业务驱动把一个业务单元封装成一项服务。这一步解决的核心问题是【高效】
业务的服务化
SOA架构特点
SOA架构和微服务架构的区别
SOA架构
微服务其实就是一个轻量级的服务治理方案
代表技术:SpringCloud、dubbo等等
微服务架构
软件架构
微服务是一种架构风格:微服务的核心就是将一站式应用,根据业务拆分成一个个的服务,每个服务提供单个业务功能的服务。
微服务这种架构风格就是把一组小服务演化成为一个单一的应用的一种方法。每个应用都运行在自己的进程中,并通过轻量级的机制保持通信,就像HTTP这样的API。这些服务要基于业务场景,并使用自动化布署工具进行独立的发布。可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储
马丁福勒
定义
单一职责功能,每个服务都很简单,只关注于一个业务功能
易于规模化开发,多个开发团队可以并行开发,每个团队负责一项服务,容器启动很快,快速加载较小的项目,这使开发人员的工作效率更高
每个单体应用不局限于固定的技术栈,开发者可以自由选择开发技术,提供API服务。
每个微服务独立的开发,部署
每个服务都可以拥有自己独立的数据库
改善故障隔离。一个服务宕机不会影响其他的服务
优点
多个微服务之间的原子事务通常是不可能的。业务需求必须包含多个微服务之间的最终一致性
数据的一致性
依赖的下游系统宕机或者由于网络问题导致服务长时间不能响应
服务的容错性
当用户对系统发起一个请求后,最上游的服务要经过多次网络传输、多次请求/应答数据解析才能完成一个整体的请求交互
系统的性能
我们必须接受自动化文化,自动化部署和自动化测试方案必须要在微服务实施开始前就规划好
小而多的应用服务必然需要更多的部署节点
实施复杂
开发人员需要处理分布式系统的复杂性
随着服务的增加,运维难度增加
系统部署服务之间的依赖关系
系统的集成测试难度增加
微服务技术栈
SpringCloud,阿里 dubbo / HSF
微服务架构有哪些?
一站式解决方案 spring cloud NetFlix
半自动 dubbo zookeeper
一站式解决方案 spring cloud Alibaba
微服务方案
远程过程调用
使用异步消息来做服务间通信。服务间通过消息管道来交换消息,从而通信。 优点: 把客户端和服务端解耦,更松耦合 提高可用性,因为消息中间件缓存了消息,直到消费者可以消费 支持很多通信机制比如通知、请求/异步响应、发布/订阅、发布/异步响应 缺点:消息中间件有额外的复杂
消息
微服务之间是如何独立通讯的?
服务提供者 provide:被其他微服务调用的微服务
服务消费者 customer :调用的其他微服务的微服务
服务提供者与服务消费者
微服务
一个业务拆分成多个子业务,每个子业务分别部署在不同的服务器上分布式一定是微服务,微服务不一定是分布式。 把一个服务拆分成多个子服务,分别放在不同的服务器上。微服务可以放在同一个服务器上,也可以放在不同的服务器上。
任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。
分布式 CAP
两阶段提交(Two-phase Commit,2PC),通过引入 协调者(Coordinator)来协调 参与者 的行为,并最终决定这些 参与者 是否要真正执行事务。
协调者询问参与者事务是否执行成功,参与者发回事务执行结果。
准备阶段
如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。
提交阶段
同步阻塞 所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
单点问题 协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。
数据不一致 在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
太过保守 任意一个节点失败就会导致整个事务失败,没有完善的容错机制。
存在的问题
两阶段提交(2PC)
TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作
Try 阶段主要是对业务系统做检测及资源预留
Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功
Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
优点: 跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些
补偿事务(TCC)
本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且使用了消息队列来保证最终一致性。
在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中。之后将本地消息表中的消息转发到 Kafka 等消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作。
优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。
本地消息表
MQ 事务消息
分布式事务
为了保证高并发情况下,同一个方法或者一个变量只能被同一个线程执行
在单体应用种 可以直接加 ReentrantLock 和 Synchronized
由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效
为什么要使用分布式锁
1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行; 2、高可用的获取锁与释放锁; 3、高性能的获取锁与释放锁; 4、具备可重入特性; 5、具备锁失效机制,防止死锁; 6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。
分布式锁应该具备哪些条件
基于数据库实现分布式锁;
分布式锁的三种实现方式
分布式
同一个业务,部署在多个服务器上
集群是个物理形态,分布式是个工作方式
分布式 是将不同的业务放在不同的服务器上面执行,集群是同一个业务同一时刻在不同的服务器上面运行
分布式实现的是缩短单个任务的执行时间来提高效率, 集群 是单位时间内执行的任务数量提高效率(高可用)
分布式:不同的业务模块部署到不同的服务器上同时执行(高并发)集群 : 同一业务在不同服务器上面执行(高可用)
分布式与集群的区别是什么?
集群
高可用
设计模式 和 微服务拆分思想
高级编程
# \"指定目录\"下创建文件夹:\"home\"目录下创建\"a\
mkdir
# \"指定\"目录下创建 a.txt文件touch /home/a.txt# \"在当前\"目录下一次性创建多个文件touch a.java b.java
touch
# \"指定\"目录下创建 a.txt文件vim /home/a.txt# \"在当前\"目录下创建文件vim a.txt
vim
常用命令
Linux操作
再有索引的情况下,增删改会有很大的影响(索引维护)。
查询简单,访问量小,查询速度影响不大。高并发时,带宽会影响查询速度。
数据库表数据变大后的影响?
Redis 是非关系型数据库,与传统的数据库不同在于它存在内存中。
数据库
缓存
消息代理
Redis应用场景
⾼性能
⾼并发
字符串,哈希,列表,集合,带范围查询的排序集合,位图,超日志,地理空间索引和流
Redis提供数据结构Key—Value
redis 只使用单核线程。平均每一个核上 redis 在存储小数据时比 memcached 性能更高memcached 可以使用多核。在 100k 以上的数据中,memcached 性能要高于 redis
性能区别
redis有五种(list set string hash zset) ⽀持更丰富的数据类型(⽀持更复杂的应⽤场景)memcached结构单一 只有String
数据类型不同
持久化
纯内存操作核心是基于非阻塞的 IO 多路复用机制单线程反而避免了多线程的频繁上下文切换问题
redis 单线程模型也能效率这么高?
Redis
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务
Swagger
Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。
正向代理类似一个跳板机,代理访问外部资源
通过VPN的方式访问就是正向代理。
正向代理
通过Nginx的方式访问就是反向代理。
保证内网的安全,阻止web攻击,大型网站,通常将反向代理作为公网访问地址,Web服务器是内网负载均衡,通过反向代理服务器来优化网站的负载
反向代理
正向代理和反向代理
轮询很容易实现,将请求按顺序轮流分配到后台服务器上,均衡的对待每一台服务器,而不关心服务器实际的连接数和当前的系统负载
轮询法(Nginx默认方式)
通过系统随机函数,根据后台服务器列表的大小值来随机选取其中一台进行访问。由概率概率统计理论可以得知,随着调用量的增大,其实际效果越来越接近于平均分配流量到后台的每一台服务器,也就是轮询法的效果。
随机法
源地址哈希法的思想是根据服务消费者请求客户端的IP地址,通过哈希函数计算得到一个哈希值,将此哈希值和服务器列表的大小进行取模运算,得到的结果便是要访问的服务器地址的序号。采用源地址哈希法进行负载均衡,相同的IP客户端,如果服务器列表不变,将映射到同一个后台服务器进行访问。
源地址哈希法
跟配置高、负载低的机器分配更高的权重,使其能处理更多的请求,而配置低、负载高的机器,则给其分配较低的权重,降低其系统负载,加权轮询很好的处理了这一问题,并将请求按照顺序且根据权重分配给后端
加权轮询法
加权随机法跟加权轮询法类似,根据后台服务器不同的配置和负载情况,配置不同的权重。不同的是,它是按照权重来随机选取服务器的,而非顺序。
加权随机
最小连接数法比较灵活和智能,由于后台服务器的配置不尽相同,对请求的处理有快有慢,它正是根据后端服务器当前的连接情况,动态的选取其中当前积压连接数最少的一台服务器来处理当前请求,尽可能的提高后台服务器利用率,将负载合理的分流到每一台服务器
最小连接数法
负债均衡
将静态页面与动态页面或者静态内容接口和动态内容接口分开不同系统访问的架构设计方法,进而提升整个服务访问性能和可维护性。
动静分离
下载——解压——自动配置(./configure)——make——make install
安装
cd /usr/local/nginx/sbin/
安装后的位置
./nginx
启动
./nginx -s stop
停止
./nginx -s quit
安全退出
./nginx -s reload
重新加载配置文件
ps -aux|grep nginx
查看进程
Nginx
注解和反射
开发中常用技巧
Java学习
0 条评论
回复 删除
下一页