JAVA 常见问题

JVM

说一下 jvm 的主要组成部分?及其作用?

JVM包括类加载子系统、堆、方法区、栈、本地方法栈、程序计数器、直接内存、垃圾回收器、执行引擎。
1、类加载子系统
类加载子系统负责加载class信息,加载的类信息存放于方法区中。
2、直接内存
直接内存是在Java堆外的、直接向系统申请的内存空间。访问直接内存的速度会高于Java堆。出于性能的考虑,读写频繁的场合可能会考虑使用直接内存。
3、垃圾回收器
垃圾回收器可以对堆、方法区、直接内存进行回收。
4、执行引擎
执行引擎负责执行虚拟机的字节码,虚拟机会使用即时编译技术将方法编译成机器码后再执行。

image
  1. 程序计数器

【指向当前线程所执行的字节码的行号】,其实就是一小块内存,记录着当前程序运行到哪了。字节码解释器的工作就是通过改变这个计数器的值来选取下一条需要执行的字节码指令(字节码指令是什么?)。分支,循环,跳转,异常处理,线程回复等都需要依赖这个计数器来完成。
由于Java的多线程是通过线程轮流切换完成的,一个线程没有执行完时就需要一个东西记录它执行到哪了,下次抢占到了CPU资源时再从这开始,这个东西就是程序计数器,正是因为这样,所以它也是“线程私有”的内存。
如果一个线程执行一个主要方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是一个本地方法,这个计数器的值则为空,此内存区域是唯一一个在Java的虚拟机规范中没有规定任何OutOfMemoryError异常情况的区域。

  1. Java虚拟机栈

与程序计数器一样,Java的虚拟机栈也是线程私有的,虚拟机栈描述的是Java的方法执行的内存模型,方法每个执行在同时的创建³³都会一个栈桢用于存储局部变量表,操作数栈,动态链接,方法出口等信息,下图为栈桢结构图:


image

通常所说的JVM里的堆和栈里这个栈就是Java的虚拟机栈,或者说是Java的虚拟机栈中的局部变量表部分。
局部变量表存放了编译期可知的各种基本数据类型,对象引用(仅限局部变量的,不包含成员变量的)。其中每个局部变量空间(Slot)有32位,所以long和double类型的数据会占用两个局部变量空间,其他类型包括对象引用占用一个。对象引用调用的是存在堆中的对象,这个引用可以是对象的起始地址或者是指向对象的句柄。局部变量表所需的内存在编译期就已经确定了也就是进入这个方法时就已经确定了,运行期间不会更改。
操作数栈则存储方法内一些进行了运算操作后的结果。
动态链接,在方法内调用接口,通过字面量链接到具体的实现类,实现Java的动态特性。
方法出口(返回地址),return或者发生Exception等。
如果方法methodOne方法调用了methodTwo,那么methodOne就会先入栈创建一个栈桢,接着methodTwo再入栈成为栈顶(假设没有其他的方法执行),methodTwo执行完先出栈,接着methodOne执行完出栈。
在使用递归的情况下,如果线程请求的栈的深度超过虚拟机所允许栈的深度就会抛出StackOverflowError;但是大部分虚拟机栈的深度都可以动态扩展,HotSpot中使用XSS可以设置栈的深度,如果扩展时无法请求到足够的内存就会抛出OutOfMemoryError。

  1. 本地方法栈

本地方法栈和虚拟机栈相似,区别就是虚拟机为虚拟机栈执行Java服务(字节码服务),而本地方法栈为虚拟机使用到的Native方法服务。本地方法栈中使用的语言,使用方式,数据结构没有强制要求。

  1. Java堆

堆是JVM里最大的一块内存区域,被所有线程共享,在虚拟机启动时创建,此区域的目的就是存放对象实例和数组,几乎所有的对象实例都在这分配(随着JIT的发展已经不是那么绝对了)的.java堆是垃圾收集管理的主要区域,由于现在收集器基本都采用分代收集方法,所以Java的堆中还可以分为新生代,老年代,永久代.1.8之后取消了永久代;其中新生代又划分为Eden空间,From Survivor空间,To Survivor空间。无论怎么划分都是为了更好的回收,分配,利用内存。下图为1.8后的内存模型
根据的Java虚拟机规范,Java的堆可以处于物理不连续的空间中,只要逻辑连续即可。在实现时,既可以实现成固定大小的也可以是可扩展的(通过-Xmx和-Xms控制),如果堆中没有足够的内存完成实例分配,并且堆也无法得到扩展时,将会抛出的OutOfMemoryError异常。

JDK8 从永久代到元空间

  1. 方法区

方法区也是一个线程共享的区域,存储已被虚拟机加载的类信息,常量(final),静态变量(static),JIT(即时编译器)编译后的代码等数据。
Java虚拟机规范把方法区描述为堆的一个逻辑部分,其实堆和方法区可以看成数据部分;虚拟机栈和程序计数器可以看成指令部分;方法区存储一些不会变更的数据,之前热点上使用GC分代收集管理方法区,所以方法区也被称为永久代(本质上两者不等价),但是现在已经使用Native Memory来代替永久代了。
虚拟机对方法区规范非常宽松,除了和Java的堆一样不需要连续的内存和可以选择固定大小意外,还可以选择不实现垃圾回收。垃圾回收行为在这个区域比较少见但还是有必要的,主要是针对常量池回收和类型的卸载。

5.1运行时常量池

运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用,运行时常量池相对于类常量池另外一个特性就是具备动态性,运行期间可能将新的常量放入池中。

说一下堆栈的区别?

队列和栈是什么?有什么区别?

什么是双亲委派模型?

当需要加载一个类的时候,子类加载器并不会马上去加载,而是依次去请求父类加载器加载,一直往上请求到最高类加载器:启动类加载器。当启动类加载器加载不了的时候,依次往下让子类加载器进行加载。当达到最底下的时候,如果还是加载不到该类,就会出现ClassNotFound的情况。

好处:保证了程序的安全性。例子:比如我们重新写了一个String类,加载的时候并不会去加载到我们自己写的String类,因为当请求上到最高层的时候,启动类加载器发现自己能够加载String类,因此就不会加载到我们自己写的String类了。

说一下类加载的执行过程?

  1. 加载:将java源代码编译后的.class字节码文件以二进制流的方式加载进内存
  2. 连接:
  • 验证:验证加载进来的二进制流是否符合虚拟机的规范,不会危害的虚拟机自身的安全
  • 准备:给类变量(静态变量)赋予初始值,基本数据/引用类型数据
  • 解析:将字符串引用转换为直接引用
  1. 初始化:变量赋予初始值、执行静态语句块、执行构造函数等等。

怎么判断对象是否可以被回收?

  1. 引用计数算法(已被淘汰的算法)
    给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
    目前主流的java虚拟机都摒弃掉了这种算法,最主要的原因是它很难解决对象
    之间相互循环引用的问题。尽管该算法执行效率很高

  2. 可达性分析算法
    目前主流的编程语言(java,C#等)的主流实现中,都是称通过可达性分析(Reachability Analysis)来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。如下图所示,对象object 5、object 6、object 7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收的对象。


    image

在Java语言中,可作为GC Roots的对象包括下面几种:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象。
  • 方法区中类静态属性引用的对象。
  • 方法区中常量引用的对象。
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象

被GC判断为”垃圾”的对象一定会回收吗?
即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。(即意味着直接回收)

如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue的队列之中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,这样做的原因是,如果一个对象在finalize()方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。

finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那基本上它就真的被回收了。

java 中都有哪些引用类型?

  • 强引用(strong reference)
    就是指在程序代码之中普遍存在的,类似“Object obj=new Object()” 这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象实例。

  • 软引用(soft reference)
    一些有用但是并非必需,用软引用关联的对象,系统将要发生OOM之前,这些对象就会被回收。

  • 弱引用(weak reference)
    弱引用(weakreference)是一些有用(程度比软引用更低)但是并非必需,用弱引用关联的对象,只能生存到下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收。

  • 虚引用(phantom reference)
    虚引用(phantomreference)幽灵引用,最弱,被垃圾回收的时候收到一个通知
    一个对象 实例是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用 来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象 实例被收集器回收时收到一个系统通知。

说一下 jvm 有哪些垃圾回收算法?

  1. 标记清除算法
    分为标记和清除两个阶段。由于标记清除算法在清理对象所占用的内存空间后,并没有整理可用的内存空间,因此如果内存中可被回收的小对象过多,则会引起内存碎片化的问题。
  2. 复制算法
  3. 标记整理法
    标记同1。标记完成后将存活的对象移到内存的另一端,然后清除该端的对象并释放内存。
  4. 分代收集算法
  5. 分区收集算法

说一下 jvm 有哪些垃圾回收器?

详细介绍一下 CMS 垃圾回收器?

新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?

  1. Serial垃圾收集器:单线程,复制算法
  2. ParNew垃圾收集器:多线程,复制算法
  3. Parrallel Scavenge垃圾收集器:多线程,复制算法
  4. Serial Old垃圾收集器:单线程,标记整理算法
  5. Parrallel Old垃圾收集器:多线程,标记整理算法
  6. CMS垃圾收集器
    CMS是为老年代设计的垃圾收集器,其主目的是使用最短的垃圾回收停顿时间完成垃圾回收,基于多线程的标记清除算法实现,以便在多线程并发环境下以最短的垃圾收集停顿时间提高系统的稳定性。
    6.1 初始标记
    标记和GC Roots直接关联的对象,需要暂停所有工作线程。
    6.2 并发标记
    和用户线程一起工作,执行GC Roots跟踪标记过程,不需要暂停工作线程。
    6.3 重新标记
    在并发标记过程中用户线程继续执行,导致在垃圾回收过程中部分的对象状态发生变化,未来保证这部分对象的状态正确性,需要对其重新标记并暂停工作线程。
    6.4 并发清除
    和用户线程一起工作,执行清除GC Roots不可达对象的任务,不需要暂停工作线程。
  7. G1垃圾收集器
    将内存划分为大小固定的几个独立区域,独立使用这些区域的内存资源并且跟踪这些区域的垃圾收集进度。同时后台维护一个优先级列表,在垃圾回收过程中根据系统允许的最长垃圾收集时间,优先回收垃圾最多的区域。
  • 基于标记整理算法,不产生内存碎片
  • 可以精确地控制停顿时间,在不牺牲吞吐量的前提下,实现最短停顿垃圾回收。

简述分代垃圾回收器是怎么工作的?

1、新生代复制算法
2、老年代标记整理算法

说一下 jvm 调优的工具?

常用的 jvm 调优的参数都有哪些?

XX比X的稳定性更差,并且版本更新不会进行通知和说明。

  1. -Xms

s为strating,表示堆内存起始大小

  1. -Xmx

x为max,表示最大的堆内存

(一般来说-Xms和-Xmx的设置为相同大小,因为当heap自动扩容时,会发生内存抖动,影响程序的稳定性)

  1. -Xmn

n为new,表示新生代大小

(-Xss:规定了每个线程虚拟机栈(堆栈)的大小)

  1. -XX:SurvivorRator=8

表示堆内存中新生代、老年代和永久代的比为8:1:1

  1. -XX:PretenureSizeThreshold=3145728

表示当创建(new)的对象大于3M的时候直接进入老年代

  1. -XX:MaxTenuringThreshold=15

表示当对象的存活的年龄(minor gc一次加1)大于多少时,进入老年代

  1. -XX:-DisableExplicirGC

表示是否(+表示是,-表示否)打开GC日志


JAVA基础

==和equals的区别

  1. 功能不同
    "=="是判断两个变量或实例是不是指向同一个内存空间。
    "equals"是判断两个变量或实例所指向的内存空间的值是不是相同。
  2. 定义不同
    "equals"在JAVA中是一个方法。
    "=="在JAVA中只是一个运算符合。

抽象类和接口的区别

含有abstract修饰符的class即为抽象类,abstract 类不能创建实例对象。含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必是抽象的。abstract class类中定义抽象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。
接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。

下面比较一下两者的语法区别:

  1. 抽象类可以有构造方法,接口中不能有构造方法。
  2. 抽象类中可以有普通成员变量,接口中没有普通成员变量
  3. 抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
  4. 抽象类中的抽象方法的访问类型可以是public,protected,但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
  5. 抽象类中可以包含静态方法,接口中不能包含静态方法
  6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
  7. 一个类可以实现多个接口,但只能继承一个抽象类。

子类与父类初始化顺序

父类静态代码块 > 子类静态代码块 > 父类普通成员变量和代码块 > 子类普通成员变量和代码块 > 父类构造函数 > 子类构造函数

JAVA中的IO流可以分为几种?

  1. 按流的方向分类:
    1. 输入流:数据流向是数据源到程序(以InputStream、Reader结尾的流)。
    2. 输出流:数据流向是程序到目的地(以OutputStream、Writer结尾的流)。
      特别注意:输入/输出流的划分是相对于程序而言的,而不是相对于数据源的。
  2. 按处理的数据单元分类:
    1. 字节流:以字节为单位获取数据,命名上以Stream结尾的流一般是字节流,如FileInputStream、FileOutputStream。字节流可以处理任何一切形式的数据源,包括音频,视频,图片,纯文本,Word,Excel等等。
    2. 字符流:以字符为单位获取数据,命名上以Reader/Writer结尾的流一般是字符流,如FileReader、FileWriter。字符流只能处理字符串,纯文本等。
  3. 按处理对象不同分类:
    1. 节点流:可以直接从数据源或目的地读写数据,如FileInputStream、FileReader、DataInputStream等。
    2. 处理流:不直接连接到数据源或目的地,是”处理流的流”。通过对其他流的处理提高程序的性能,如BufferedInputStream、BufferedReader等。处理流也叫包装流。
      节点流处于IO操作的第一线,所有操作必须通过它们进行;处理流可以对节点流进行包装,提高性能或提高程序的灵活性。没有节点流,处理流发挥不了任何作用。

BIO、NIO、AIO有什么区别

  • BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
  • NIO (New I/O): NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发
  • AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。


    image
  • IO:一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理。线程开销大。
  • 伪异步 IO:将请求连接放入线程池,一对多,但线程还是很宝贵的资源。
  • NIO:一个请求一个线程,但客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有 I/O 请求时才启动一个线程进行处理。
  • AIO:一个有效请求一个线程,客户端的 I/O 请求都是由 OS 先完成了再通知服务器应用去启动线程进行处理,
  • BIO 是面向流的,NIO 是面向缓冲区的;BIO 的各种流是阻塞的。而 NIO 是非阻塞的;BIO的 Stream 是单向的,而 NIO 的 channel 是双向的。
  • NIO 的特点:事件驱动模型、单线程处理多任务、非阻塞 I/O,I/O 读写不再阻塞,而是返回 0、基于 block 的传输比基于流的传输更高效、更高级的 IO 函数 zero-copy、IO 多路复用大大提高了 Java 网络应用的可伸缩性和实用性。基于 Reactor 线程模型。
    在 Reactor 模式中,事件分发器等待某个事件或者可应用或个操作的状态发生,事件分发器就把这个事件传给事先注册的事件处理函数或者回调函数,由后者来做实际的读写操作。如在 Reactor 中实现读:注册读就绪事件和相应的事件处理器、事件分发器等待事件、事件到来,激活分发器,分发器调用事件对应的处理器、事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

容器

Colletion和Collections有什么区别

Collection是集合体系的最顶层,包含了集合体系的共性
Collections是一个工具类,方法都是用于操作Collection

List、Set、Map之间的区别

image

List 可重复

  • ArrayList: 基于数组实现,增删慢,查询快,线程不安全
  • Vector:基于数组实现,增删慢,查询快,线程安全
    Vector的数据结构和ArrayList是一样的,不同的是Vector支持线程同步,即同一时刻只允许一个线程对Vector进行写操作。因为需要在Vector实例进行加锁和释放锁,其读写效率要比ArrayList低。
  • LinkedList:基于双向链表实现,增删快,查询慢,线程不安全

Queue

  • ArrayBlockingQueue:基于数组结构实现的有界阻塞队列
  • LinkedBlockingQueue:基于链表数据结构实现的有界阻塞队列
  • PriorityBlockingQueue:支持优先级排序的无界阻塞队列
  • DelayQueue:支持延迟操作的无界阻塞队列
  • SynchronouesQueue:用于线程同步的阻塞队列
  • LinkedTransferQueue:基于链表数据结构实现的无界阻塞队列
  • LinkedBlockingQueue:基于链表数据结构实现的双向阻塞队列

Set 不可重复

  • HashSet:HashMap实现,无序。先判断元素hashcode,如果相同再判断equals
  • TreeSet:二叉树实现。自定义的数据类型必须实现Comparable接口,并且覆写其中的compareTo函数才可以按照预定义的顺序存储。如果是升序排序,则this对象小于指定对象的情况下,返回-1。降序反之。
  • LinkHashSet:继承HashSet,HashMap实现数据存储,双向链表记录顺序

Map

  • HashMap 数组+链表存储结构,线程不安全
    [HashMap解析]https://www.jianshu.com/p/1675d386f1af

  • ConcurrentHashMap:
    1.7之前采用segment分段锁。
    1.8之后采用Synchronized+CAS,同时引入了红黑树。取消segments字段,直接采用transient volatile HashEntry<K,V>[] table保存数据,采用table数组元素作为锁,从而实现了对每一行数据进行加锁,并发控制使用Synchronized和CAS来操作
    将原先table数组+单向链表的数据结构,变更为table数组+单向链表+红黑树的结构.

  • HashTable: 线程安全。他是遗留类,他的常用功能和hashmap类似。不同的是他继承自Dictionary类。同一时刻,只能有一个线程写HashTable,并发性不如ConcurrentMap。

  • TreeMap:基于二叉树结构。其Key必须实现comparable接口或者自定义的比较器。

  • LinkedHashMap:继承HashMap,使用链表保存插入顺序

怎么确保一个集合不能被修改?

  • Collections. unmodifiableCollection(Collection c) 方法创建的集合。
  • 使用Arrays.asList创建的集合。

多线程

并行和并发有什么区别?

你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。

进程和线程有什么区别

进程是资源分配的最小单位,线程是CPU调度的最小单位

sleep和wait的区别

  • sleep属于Thread类,wait属于Object类
  • sleep方法暂停执行的指定时间,让出CPU给其他线程,但其监控状态依然保持,在指定的时间过后又会自动恢复运行状态。
  • 调用sleep过程中,线程不会释放对象锁;调用wait方法时,线程会释放对象锁,进入等待此对象的等待锁池,只有针对此对象调用notify方法后,该线程才能进入对象锁池准备获取对象锁,并进入运行状态。

start与run的区别

用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。

创建线程有哪几种方式?

  • 继承Thread类创建线程类
  • 通过Runnable接口创建线程类
  • 通过Callable和Future创建线程

创建线程的三种方式的对比:
采用实现Runnable、Callable接口的方式创见多线程时,

  • 优势:线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
    在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
  • 劣势:编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

使用继承Thread类的方式创建多线程时

  • 优势:编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
  • 劣势:线程类已经继承了Thread类,所以不能再继承其他父类。

说一下 runnable 和 callable 有什么区别?

  • Callable规定的方法是call(),Runnable规定的方法是run().
  • Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
  • call方法可以抛出异常,run方法不可以
  • 运行Callable任务可以拿到一个Future对象,Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如果线程没有执行完,Future.get()方法可能会阻塞当前线程的执行;如果线程出现异常,Future.get()会throws InterruptedException或者ExecutionException;如果线程已经取消,会跑出CancellationException。取消由cancel 方法来执行。isDone确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明Future<?> 形式类型、并返回 null 作为底层任务的结果。

创建线程池有哪几种方式?

  • newFixedThreadPool:定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程
  • newCachedThreadPool:可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制
  • newScheduledThreadPool:定长线程池,可执行周期性的任务
  • newSingleThreadExecutor: 单线程的线程池,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行
  • newSingleThreadScheduledExecutor: 单线程可执行周期性任务的线程池
  • newWorkStealingPool:任务窃取线程池,不保证执行顺序,适合任务耗时差异较大。

线程池中 submit()和 execute()方法有什么区别?

  • execute() 参数 Runnable ;submit() 参数 (Runnable) 或 (Runnable 和 结果 T) 或 (Callable)
  • execute() 没有返回值;而 submit() 有返回值
  • submit() 的返回值 Future 调用get方法时,可以捕获处理异常

什么是死锁?怎么防止死锁?

死锁的四个条件

  1. 互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放
  2. 请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用
  4. 循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

怎么避免死锁

  1. 避免嵌套锁
  2. 只锁需要的部分
  3. 避免无限期等待

ThreadLocal 是什么?有哪些使用场景?

  1. 介绍

在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。
ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。
在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。

  1. 作用

ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

  1. ThreadLocal的应用场景?

在Java的多线程编程中,为保证多个线程对共享变量的安全访问,通常会使用synchronized来保证同一时刻只有一个线程对共享变量进行操作。这种情况下可以将类变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象。最常见的ThreadLocal使用场景为用来解决数据库连接、Session管理等。在下面会例举几个场景。

经典的使用场景是为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的 Connection; 还有 Session 管理 等问题。

  1. ThreadLocal 与 Synchronized区别

相同:ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
不同:Synchronized同步机制采用了“以时间换空间”的方式,仅提供一份变量,让不同的线程排队访问;而ThreadLocal采用了“以空间换时间”的方式,每一个线程都提供了一份变量,因此可以同时访问而互不影响。
以时间换空间->即枷锁方式,某个区域代码或变量只有一份节省了内存,但是会形成很多线程等待现象,因此浪费了时间而节省了空间。
以空间换时间->为每一个线程提供一份变量,多开销一些内存,但是呢线程不用等待,可以一起执行而相互之间没有影响。

小结:ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

说一下 synchronized 底层实现原理?(待补充)

synchronized关键字作用于Java对象、方法、代码块提供线程安全的操作。它属于独占式的悲观锁,同时属于可重入锁。

  • 在它修饰对象时,同一时刻只能有一个线程对该对象进行访问。
  • 在它修饰方法、代码块时,同一时刻只能有一个线程执行该方法或者代码块。

synchronized、volatile 、Lock、ReentrantLock 区别是什么?

  1. 并发编程中的三个概念:
  • 原子性:即一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
  • 可见性:是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
  • 有序性:即程序执行的顺序按照代码的先后顺序执行。
  1. 指令重排序

一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。

  1. 在java内存模型中,也会存在缓存一致性问题和指令重排序的问题。

Java内存模型规定所有的变量都是存在主存当中(类似于前面说的物理内存),每个线程都有自己的工作内存(java栈中的帧)(类似于高速缓存)。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。
Java内存模型只保证了基本读取和赋值是原子性操作,如果要实现更大范围操作的原子性,可以通过synchronized和Lock来实现。

  1. 同步的概念:

Java中的同步是指通过人为的控制和调度,保证共享资源在多线程访问下是安全的,即保证结果是正确的。

volatile

  • volatile 是一个类型修饰符。volatile 的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略.
    volatile 的特性保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(实现可见性)
    禁止进行指令重排序。(实现有序性)
  • volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。
    关于volatile 原子性可以理解为把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步。
  • volatile没办法保证对变量的操作的原子性。保证原子性操作:采用synchronized,采用Lock,采用AtomicInteger。使用volatile必须具备以下2个条件【保证原子性】:1)对变量的写操作不依赖于当前值。2)该变量没有包含在具有其他变量的不变式中。

volatile 与 synchronized对比:

  1. volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.
  2. volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.
  3. volatile仅能实现变量的修改可见性,而synchronized则可以保证变量的修改可见性和原子性.
      《Java编程思想》上说,定义long或double变量时,如果使用volatile关键字,就会获得(简单的赋值与返回操作)原子性。
  4. volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.
  5. 当一个域的值依赖于它之前的值时,volatile就无法工作了,如n=n+1,n++等。如果某个域的值受到其他域的值的限制,那么volatile也无法工作,如Range类的lower和upper边界,必须遵循lower<=upper的限制。
  6. 使用volatile而不是synchronized的唯一安全的情况是类中只有一个可变的域。

synchronized和Lock区别

  1. Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
  2. synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
  3. Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
  4. synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。
    通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
  5. Lock可以提高多个线程进行读操作的效率。
  6. 在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

synchronized和ReentrantLock区别

相似点:

这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善)。

功能区别:

这两种方式最大区别就是对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成

便利性:

很明显Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。

锁的细粒度和灵活度:

很明显ReenTrantLock优于Synchronized

性能的区别:

在Synchronized优化以前,synchronized的性能是比ReenTrantLock差很多的,但是自从Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized,其实synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。

ReentrantLock

由于ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了一些高级功能,主要有以下3项:

  1. 等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。通过lock.lockInterruptibly()来实现这个机制。
  2. 公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。
    公平锁、非公平锁的创建方式:
//创建一个非公平锁,默认是非公平锁
Lock lock = new ReentrantLock();
Lock lock = new ReentrantLock(false);
 
//创建一个公平锁,构造传参true
Lock lock = new ReentrantLock(true);
  1. 锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象。ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。

对象拷贝

为什么要使用克隆?

如何实现对象克隆?

深拷贝和浅拷贝区别是什么?


Java Web

jsp 和 servlet 有什么区别?

session 和 cookie 有什么区别?

  1. cookie
    由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。
    Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
  2. session
    Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。
    如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。

提示:Session的使用比Cookie方便,但是过多的Session存储在服务器内存中,会对服务器造成压力。

  1. 区别
  • cookie数据存放在客户的浏览器上,session数据放在服务器上.
  • cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。
  • 设置cookie时间可以使cookie过期。但是使用session-destory(),我们将会销毁会话。
  • session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用cookie。
  • 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。(Session对象没有对存储的数据量的限制,其中可以保存更为复杂的数据类型)

注意:
1.session很容易失效,用户体验很差;
2.虽然cookie不安全,但是可以加密 ;
3.cookie也分为永久和暂时存在的;
4.浏览器 有禁止cookie功能 ,但一般用户都不会设置;
5.一定要设置失效时间,要不然浏览器关闭就消失了;
例如:
记住密码功能就是使用永久cookie写在客户端电脑,下次登录时,自动将cookie信息附加发送给服务端。
application是全局性信息,是所有用户共享的信息,如可以记录有多少用户现在登录过本网站,并把该信息展示个所有用户。
两者最大的区别在于生存周期,一个是IE启动到IE关闭.(浏览器页面一关 ,session就消失了),一个是预先设置的生存周期,或永久的保存于本地的文件。(cookie)

说一下 session 的工作原理?

如果客户端禁止 cookie 能实现 session 还能用吗?

一般默认情况下,在会话中,服务器存储 session 的 sessionid 是通过 cookie 存到浏览器里。

如果浏览器禁用了 cookie,浏览器请求服务器无法携带 sessionid,服务器无法识别请求中的用户身份,session失效。

但是可以通过其他方法在禁用 cookie 的情况下,可以继续使用session:

  • 通过url重写,把 sessionid 作为参数追加的原 url 中,后续的浏览器与服务器交互中携带 sessionid 参数。
  • 服务器的返回数据中包含 sessionid,浏览器发送请求时,携带 sessionid 参数。
  • 通过 Http 协议其他 header 字段,服务器每次返回时设置该 header 字段信息,浏览器中 js 读取该 header 字段,请求服务器时,js设置携带该 header 字段。

spring mvc 和 struts 的区别是什么?

如何避免 sql 注入?

什么是 XSS 攻击,如何避免?

什么是 CSRF 攻击,如何避免?


异常

throw 和 throws 的区别?

  • throw:

表示方法内抛出某种异常对象
如果异常对象是非 RuntimeException 则需要在方法申明时加上该异常的抛出 即需要加上 throws 语句 或者 在方法体内 try catch 处理该异常,否则编译报错
执行到 throw 语句则后面的语句块不再执行

  • throws:

方法的定义上使用 throws 表示这个方法可能抛出某种异常
需要由方法的调用者进行异常处理

final、finally、finalize 有什么区别?

final可以用来修饰类、方法、变量,分别有不同的意义所在,final修饰的class代表不可继续扩展,final修饰的变量代表不可修改,final修饰的方法代表不可重写。

finally则是java保证某一段重点代码一定要被执行的修饰符,例如:我们需要用try块让JDBC保证连接,保证unlock锁等动作

finalize是基础类java.lang.Object的一个方法,它的设计目的是为了保证对象在垃圾回收之前完成特定资源的回收

try-catch-finally 中哪个部分可以省略?

try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

常见的异常类有哪些?


网络

http 响应码 301 和 302 代表的是什么?有什么区别?

  • 301 Moved Permanently

被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。

  • 302 Found

请求的资源现在临时从不同的 URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。

forward 和 redirect 的区别?

tcp三次握手

image

字段 含义
URG 紧急指针是否有效。为1,表示某一位需要被优先处理
ACK 确认号是否有效,一般置为1。
PSH 提示接收端应用程序立即从TCP缓冲区把数据读走。
RST 对方要求重新建立连接,复位。
SYN 请求建立连接,并在其序列号的字段进行序列号的初始值设定。建立连接,设置为1
FIN 希望断开连接。

image

一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

tcp 为什么要三次握手,两次不行吗?为什么?

TCP,UDP区别?

  • TCP 是可靠通信协议, 而 UDP 是不可靠通信协议。

TCP 的可靠性含义: 接收方收到的数据是完整, 有序, 无差错的。
UDP 不可靠性含义: 接收方接收到的数据可能存在部分丢失, 顺序也不一定能保证。
UDP 和 TCP 协议都是基于同样的互联网基础设施, 且都基于 IP 协议实现, 互联网基础设施中对于数据包的发送过程是会发生丢包现象的, 为什么 TCP 就可以实现可靠传输, 而 UDP 不行?

TCP 协议为了实现可靠传输, 通信双方需要判断自己已经发送的数据包是否都被接收方收到, 如果没收到, 就需要重发。 为了实现这个需求, 很自然地就会引出序号(sequence number) 和 确认号(acknowledgement number) 的使用。

发送方在发送数据包(假设大小为 10 byte)时, 同时送上一个序号( 假设为 500),那么接收方收到这个数据包以后, 就可以回复一个确认号(510 = 500 + 10) 告诉发送方 “我已经收到了你的数据包, 你可以发送下一个数据包, 序号从 510 开始” 。

这样发送方就可以知道哪些数据被接收到,哪些数据没被接收到, 需要重发。

说一下 tcp 粘包是怎么产生的?

  1. TCP粘包是什么?
    粘包发生在发送或接收缓冲区中;应用程序从缓冲区中取数据是整个缓冲区中有多少取多少;那么就有可能第一个数据的尾部和第二个数据的头部同时存在缓冲区,而TCP是流式的,数据无边界,这时发生粘包。


    image
  2. TCP粘包的产生
    2.1 发送方产生粘包
    采用TCP协议传输数据的客户端与服务器经常是保持一个长连接的状态(一次连接发一次数据不存在粘包),双方在连接不断开的情况下,可以一直传输数据;但当发送的数据包过于的小时,那么TCP协议默认的会启用Nagle算法,将这些较小的数据包进行合并发送(缓冲区数据发送是一个堆压的过程);这个合并过程就是在发送缓冲区中进行的,也就是说数据发送出来它已经是粘包的状态了;


    image

    2.2 接收方产生粘包
    接收方采用TCP协议接收数据时的过程是这样的:数据到底接收方,从网络模型的下方传递至传输层,传输层的TCP协议处理是将其放置接收缓冲区,然后由应用层来主动获取(C语言用recv、read等函数);这时会出现一个问题,就是我们在程序中调用的读取数据函数不能及时的把缓冲区中的数据拿出来,而下一个数据又到来并有一部分放入的缓冲区末尾,等我们读取数据时就是一个粘包;(放数据的速度 > 应用层拿数据速度)


    image
  3. TCP粘包解决方案
    目前应用最广泛的是在消息的头部添加数据包长度,接收方根据消息长度进行接收;在一条TCP连接上,数据的流式传输在接收缓冲区里是有序的,其主要的问题就是第一个包的包尾与第二个包的包头共存接收缓冲区,所以根据长度读取是十分合适的;

1.解决发送方粘包
(1)发送产生是因为Nagle算法合并小数据包,那么可以禁用掉该算法;
(2)TCP提供了强制数据立即传送的操作指令push,当填入数据后调用操作指令就可以立即将数据发送,而不必等待发送缓冲区填充自动发送;
(3)数据包中加头,头部信息为整个数据的长度(最广泛最常用);
2.解决接收方粘包
(1)解析数据包头部信息,根据长度来接收;
(2)自定义数据格式:在数据中放入开始、结束标识;解析时根据格式抓取数据,缺点是数据内不能含有开始或结束标识;
(3)短连接传输,建立一次连接只传输一次数据就关闭;(不推荐)

OSI 的七层模型都有哪些?

get 和 post 请求有哪些区别?

如何实现跨域?

说一下 JSONP 实现原理?

--

设计模式

  1. 设计模式7个原则
  • 单一职责原则
    单一职责原则又称单一功能原则,它规定一个类只有一个职责。如果有多个职责(功能)被设计在一个类中,这个类就违反了单一职责原则。
  • 开闭原则
    开闭原则规定软件中的对象(类、模块、函数等)对扩展开放,对修改开闭,这意味着一个实体允许在不改变其源代码的前提下改变其行为,该特性在产品化的环境下是特别有价值的,在这种环境下,改变源代码需要经过代码审查、单元测试等过程,以确保产品的使用质量。遵循这个原则的代码在扩展时不会发生改变,因此不需要经历上述过程。
  • 里氏替换原则
    里氏替换原则是对开闭原则的补充,规定了在任意父类可以出现的地方,子类都一定可以出现。实现开闭原则的关键是抽象化,父类与子类的继承关系就是抽象化的具体表现,所以里氏替换原则是对实现抽象化的具体步骤规范。
  • 依赖倒换原则
    依赖倒换原则指程序要依赖于对象(比如JAVA中的抽象类和接口),而不依赖于具体的实现。简单地说,就是基于抽象进行编程,不要求对实现进行编程,这就降低了模块之间的耦合度。
  • 接口隔离原则
    接口隔离原则指通过将不同的功能定义在不同的接口中来实现接口的隔离,这样就避免了其他类在依赖该接口时(接口上定义的功能)依赖其不需要的接口,可减少接口之前的依赖冗余性和复杂性。
  • 合成、聚合复用原则
    合成、聚合复用原则指通过一个在新的对象引入(注入)已有的对象以达到累的功能复用和扩展的目的。它的设计原则是要尽量使用合成或聚合,而不要使用继承来扩展类的功能。
  • 迪米特法则
    迪米特法则指一个对象尽可能少地与其他对象发生相互作用,即一个对象对其他对象应该有尽可能少的了解或依赖。其核心思想在于降低模块之间的耦合度,提高模块的内聚性。迪米特法则容易使系统模块之间功能独立,使得各个模块的独立运行变得更简单,同时使得各个模块之间的组合变得更容易。
  1. 三大类设计模式
  • 创建型模式

工厂模式
抽象工厂模式
单例模式

懒汉模式->双重校验锁
饿汉模式

建造者模式
原型模式

  • 结构性模式

适配器模式
桥接模式
过滤器模式
组合模式
装饰器模式
外观模式
亨元模式
代理模式

  • 行为型模式

责任链模式
命令模式
解释器模式
迭代器模式
中介者模式
备忘录模式
观察则模式
状态模式
策略模式
模板模式
访问者模式


Spring

为什么要使用 spring?

解释一下什么是 aop?

解释一下什么是 ioc?

Inversion of Control

Spring的控制反转指一个对象依赖的其他对象会在容器的初始化完成后主动将其依赖的对象传递给它,而不需要这个对象自己创建或者查找其依赖的对象。Spring中的实例对象可以使全局唯一的单例模式,也可以在每次需要时都重新生成一个新的实例,具体可以哪种方式创建Bean的生命周期决定,通过protoType属性来定义。

spring 有哪些主要模块?

  1. 核心容器层
  • Spring-Beans
  • Spring-Core
  • Spring-Context
  • SpEL
    SpEL模块提供了丰富的表达式语言支持,用于在运行过程中查询和操作对象实例。SpEL在JSP 2.1表达式语言规范的基础上进行了扩展,支持set、get方法、属性赋值、方法调用、访问数组集合、索引内容、逻辑算术运算、明明变量、基于名称在Spring IoC容器检索对象,同时支持列表的投影、选择和聚合等功能。
  1. 数据访问层
  • JDBC
  • ORM(Object Relational Mapping)
    ORM模块提供了对象关系映射API的集成,包括JPA、JDO和Hibernate等。基于该模块,ORM框架能很容易地和Spring的其他功能整合,例如事务管理。
  • OXM
    OXM模块提供了对OXM实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。
  • JMS
    JMS模块包含消息的生产、消费功能。从Spring4.1开始,Spring集成了Spring-Messaging模块,用于实现对消息队列的支持。
  • 事务处理
  1. web应用层
  • Web
    Web模块不但提供了面向Web应用的基本功能,还提供了HTTP客户端及Spring远程调用中与Web相关的部分。
    Web模块基于Servlet监听器初始化IoC容器。
  • Web-MVC
    Web-MVC模块为Web应用提供了模型视图控制和REST API服务的实现。Spring的MVC框架使数据模型和视图分离,数据模型负责数据的业务逻辑,视图负责数据的展示。同时,Web-MVC可与Spring框架的其他模块方便地集成。
  • Web-Socket
  • Web-Portlet
  1. 其他重要模块
  • AOP
  • Aspects
  • Instrumentation
  • Messaging
  • Test

spring 常用的注入方式有哪些?

  1. 构造方法注入
  2. set注入
  3. 注解注入

spring 中的 bean 是线程安全的吗?

结论: 不是线程安全的

Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。
Spring 的 bean 作用域(scope)类型:

  1. singleton:单例,默认作用域。
  2. prototype:原型,每次创建一个新对象。
  3. request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下。
  4. session:会话,同一个会话共享一个实例,不同会话使用不用的实例。
  5. global-session:全局会话,所有会话共享一个实例。

线程安全这个问题,要从单例与原型Bean分别进行说明。
原型Bean

对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。

单例Bean

对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。
如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。

注:
有状态就是有数据存储功能
无状态就是不会保存数据

spring 支持几种 bean 的作用域?

见上边问题

spring 自动装配 bean 有哪些方式?

  • no:默认方式,手动装配方式,需要通过ref设定bean的依赖关系
  • byName:根据bean的名字进行装配,当一个bean的名称和其他bean的属性一致,则自动装配
  • byType:根据bean的类型进行装配,当一个bean的属性类型与其他bean的属性的数据类型一致,则自动装配
  • constructor:根据构造器进行装配,与 byType 类似,如果bean的构造器有与其他bean类型相同的属性,则进行自动装配
  • autodetect:如果有默认构造器,则以constructor方式进行装配,否则以byType方式进行装配

spring 事务实现方式有哪些?

Spring提供了编程式事务和声明式事务两种实现方式。

  • 编程式事务允许用户在代码中精确定义事务的边界,
  • 声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。

简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;
而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。

说一下 spring 的事务隔离?

spring 有五大隔离级别

  • 默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致 (越往下隔离级别越高,花费越大):
  • read uncommited 未提交读:是最低的事务隔离级别。事务未提交前,数据就可被其他事务读取 --> 会造成:脏读、不可重复读、幻读
  • read commited 提交读:一个事务提交后才能被其他事务读取到,SQL server 的默认级别 --> 会造成:不可重复读、幻读
  • repeatable read 可重复读:保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据,MySQL 的默认级别 --> 会造成: 幻读。
  • serializable 序列化:这是花费最高代价但最可靠的事务隔离级别。事务被处理为顺序执行。该隔离级别能防止脏读、不可重复读、幻读。
  • 脏读:表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。因为这个记录 A还没有提交那么另外一个事务读取到的这个数据我们称之为脏数据。依据脏数据所做的操作肯能是不正确的。
  • 不可重复读:指在一个事务内,多次读同一数据,但不一样。例如,事务1 读取 记录A, 事务1 还没有执行结束;此时另外一个 事务2 也访问 记录A,并修改了记录A。 那么在事务1 第两次读取记录A时,读到的数据可能是不一样的。这种情况被称为是不可重复读。
  • 幻象读:指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。

说一下 spring mvc 运行流程?

image

执行流程:

  1. 用户向服务器发送请求,请求被 Spring 前端控制 Servelt DispatcherServlet 捕获(捕获)
  2. DispatcherServlet对请求 URL进行解析,得到请求资源标识符(URI)。然后根据该 URI,调用 HandlerMapping获得该Handler配置的所有相关的对象(包括 Handler对象以及 Handler对象对应的拦截器),最后以 HandlerExecutionChain对象的形式返回;(查找 handler)
  3. DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。 提取Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller), Handler执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象(执行 handler)
  4. DispatcherServlet 根据返回的 ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到 Spring 容器中的 ViewResolver) (选择 ViewResolver)
  5. 通过 ViewResolver 结合 Model 和 View,来渲染视图,DispatcherServlet 将渲染结果返回给客户端。(渲染返回)

快速记忆技巧:

核心控制器捕获请求、查找Handler、执行Handler、选择ViewResolver,通过ViewResolver渲染视图并返回

spring mvc 有哪些组件?

@RequestMapping 的作用是什么?

@Autowired 的作用是什么?


Spring Boot/Spring Cloud

什么是 spring boot?

为什么要用 spring boot?

spring boot 核心配置文件是什么?

spring boot 配置文件有哪几种类型?它们有什么区别?

spring boot 有哪些方式可以实现热部署?

jpa 和 hibernate 有什么区别?

什么是 spring cloud?

spring cloud 断路器的作用是什么?

spring cloud 的核心组件有哪些?


Hibernate

为什么要使用 hibernate?

什么是 ORM 框架?

对象-关系映射(Object-Relational Mapping,简称ORM),面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。

hibernate 中如何在控制台查看打印的 sql 语句?

hibernate 有几种查询方式?

hibernate 实体类可以被定义为 final 吗?

在 hibernate 中使用 Integer 和 int 做映射有什么区别?

hibernate 是如何工作的?

get()和 load()的区别?

说一下 hibernate 的缓存机制?

hibernate 对象有哪些状态?

在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?

hibernate 实体类必须要有无参构造函数吗?为什么?


Mybatis

mybatis 中 #{}和 ${}的区别是什么?

mybatis 有几种分页方式?

RowBounds 是一次性查询全部结果吗?为什么?

mybatis 逻辑分页和物理分页的区别是什么?

mybatis 是否支持延迟加载?延迟加载的原理是什么?

说一下 mybatis 的一级缓存和二级缓存?

mybatis 和 hibernate 的区别有哪些?

mybatis 有哪些执行器(Executor)?

mybatis 分页插件的实现原理是什么?

mybatis 如何编写一个自定义插件?


RabbitMQ

rabbitmq 的使用场景有哪些?

rabbitmq 有哪些重要的角色?

rabbitmq 有哪些重要的组件?

rabbitmq 中 vhost 的作用是什么?

rabbitmq 的消息是怎么发送的?

rabbitmq 怎么保证消息的稳定性?

rabbitmq 怎么避免消息丢失?

要保证消息持久化成功的条件有哪些?

rabbitmq 持久化有什么缺点?

rabbitmq 有几种广播类型?

rabbitmq 怎么实现延迟消息队列?

rabbitmq 集群有什么用?

rabbitmq 节点的类型有哪些?

rabbitmq 集群搭建需要注意哪些问题?

rabbitmq 每个节点是其他节点的完整拷贝吗?为什么?

rabbitmq 集群中唯一一个磁盘节点崩溃了会发生什么情况?

rabbitmq 对集群节点停止顺序有要求吗?


Kafka

kafka 可以脱离 zookeeper 单独使用吗?为什么?

kafka 有几种数据保留的策略?

kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka 将如何处理?

什么情况会导致 kafka 运行变慢?

使用 kafka 集群需要注意什么?


Zookeeper

zookeeper 是什么?

zookeeper 都有哪些功能?

zookeeper 有几种部署模式?

zookeeper 怎么保证主从节点的状态同步?

集群中为什么要有主节点?

集群中有 3 台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗?

说一下 zookeeper 的通知机制?


MySql

数据库的三范式是什么?

一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?

一般情况下,我们创建的表的类型是InnoDB,如果新增一条记录(不重启mysql的情况下),这条记录的id是8;但是如果重启(文中提到的)MySQL的话,这条记录的ID是6。因为InnoDB表只把自增主键的最大ID记录到内存中,所以重启数据库或者对表OPTIMIZE操作,都会使最大ID丢失。

但是,如果我们使用表的类型是MylSAM,那么这条记录的ID就是8。因为MylSAM表会把自增主键的最大ID记录到数据文件里面,重启MYSQL后,自增主键的最大ID也不会丢失。

注:如果在这7条记录里面删除的是中间的几个记录(比如删除的是3,4两条记录),重启MySQL数据库后,insert一条记录后,ID都是8。因为内存或者数据库文件存储都是自增主键最大ID

如何获取当前数据库版本?

说一下 ACID 是什么?

char 和 varchar 的区别是什么?

float 和 double 的区别是什么?

mysql 的内连接、左连接、右连接有什么区别?

mysql 索引是怎么实现的?

  1. 按表列属性分类:
  • 单列索引
    以表的单个列字段创建的索引
  • 联合索引
    以表的多个列字段组合创建的索引,在查询条件使用索引的从左字段顺序才会生效,遵循最左匹配原则。

单列索引和联合索引又包括:

  • 普通索引
    非主键,非唯一列的索引
  • 主键索引
    基于该表主键自动生成成的索引,如果未给表定义主键,会查找该表中是否存在非空、整形、唯一索引作为其主键(可通过select _rowid from 表名查看),若都不满足会隐式生成一个rowid作为主键(无法直接查到)
  • 唯一索引
    基于表的唯一列生成的索引,允许为空值
  • 全文索引
    将存储于数据库中的整本书或整篇文章中任意内容信息查找出来,如大量级的文字中如like %关键字%,普通索引的效率与全文索引相比是非常低的。
  1. 按数据结构分类:
  • B+tree索引
    b+tree基于平衡二叉树的一种多路平衡查找树,所有记录都按照顺序存放在叶子节点中,各个叶子节点直接通过链表相连。与b树不同的是:1、非叶子节点只存储键值信息。2、所有叶子节点之间都有一个链指针。3、数据记录都存放在叶子节点中。
  • hash索引
    基于hash表结构实现的索引,mysql中只有MEMORY/HEAP和NDB存储引擎支持;
    InnoDB引擎支持自适应hash索引,但是是数据库自身创建使用的,而不能进行人为定义。当二级索引被频繁的访问时,便会自动创建自适应哈希索引;
    通过 命令SHOW ENGINE INNODB STATUS可查看自适应hash索引的使用情况;
    通过 命令SHOW VARIABLES LIKE '%ap%hash_index' 查看是否打开自适应hash索引。

对比:

  • 由于hash索引是比较其hash值,hash索引只能进行等值查找而不能进行范围查找
  • hash索引无法进行排序:原因同上
    -> 不支持最左匹配原则,复合索引时合并一起计算hash值
  • hash索引的检索效率很高可以一次定位,但是当发生大量hash碰撞的时候,链表变长,hash索引效率上是不如b+tree的
  • 由于存在hash碰撞的问题,当需要获得总数时候,hash 索引在任何时候都不能避免表扫描
  • T-tree索引
  • R-tree索引
  1. 按存储结构分类:
  • 聚簇索引(聚集索引)
    InnoDB的聚簇索引实际上是在同一个BTree结构中同时存储了索引和整行数据,通过该索引查询可以直接获取查询数据行。
    聚簇索引不是一种单独的索引类型,而是一种数据的存储方式,聚簇索引的顺序,就是数据在硬盘上的物理顺序。
    在mysql通常聚簇索引是主键的同义词,每张表只包含一个聚簇索引(其他数据库不一定)。
  • 辅助索引(非聚集索引,次级索引,二级索引)
    非聚集索引在BTree的叶子节点中保存了索引列和主键。如果查询列不在该索引内,只能查到其主键值,还需要回表操作查询聚簇索引进行查询。

聚簇索引的优点:

  • 可以把相关数据保存在一起,如:实现电子邮箱时,可以根据用户ID来聚集数据,这样只需要从磁盘读取>- 少量的数据页就能获取某个用户全部邮件,如果没有使用聚集索引,则每封邮件都可能导致一次磁盘IO
    数据访问更快,聚集索引将索引和数据保存在同一个btree中,因此从聚集索引中获取数据通常比在非聚集索引中查找要快
  • 使用覆盖索引扫描的查询可以直接使用页节点中的主键值

聚簇索引的缺点:

  • 聚簇数据最大限度地提高了IO密集型应用的性能,但如果数据全部放在内存中,则访问的顺序就没有那么重要了,聚集索引也没有什么优势了
  • 插入速度严重依赖于插入顺序,按照主键的顺序插入是加载数据到innodb表中速度最快的方式,但如果不是按照主键顺序加载数据,那么在加载完成后最好使用optimize table命令重新组织一下表
  • 更新聚集索引列的代价很高,因为会强制innodb将每个被更新的行移动到新的位置
  • 基于聚集索引的表在插入新行,或者主键被更新导致需要移动行的时候,可能面临页分裂的问题,当行的主键值要求必须将这一行插入到某个已满的页中时,存储引擎会将该页分裂成两个页面来容纳该行,这就是一次页分裂操作,页分裂会导致表占用更多的磁盘空间
  • 聚集索引可能导致全表扫描变慢,尤其是行比较稀疏,或者由于页分裂导致数据存储不连续的时候
  • 二级索引可能比想象的更大,因为在二级索引的叶子节点包含了引用行的主键列。
  • 二级索引访问需要两次索引查找,而不是一次

怎么验证 mysql 的索引是否满足需求?

说一下数据库的事务隔离?

说一下 mysql 常用的引擎?

  1. MyISAM存储引擎:不支持事务、也不支持外键,优势是访问速度快,对事务完整性没有 要求或者以select,insert为主的应用基本上可以用这个引擎来创建表
    支持3种不同的存储格式,分别是:静态表;动态表;压缩表
  • 静态表:表中的字段都是非变长字段,这样每个记录都是固定长度的,优点存储非常迅速,容易缓存,出现故障容易恢复;缺点是占用的空间通常比动态表多(因为存储时会按照列的宽度定义补足空格)ps:在取数据的时候,默认会把字段后面的空格去掉,如果不注意会把数据本身带的空格也会忽略。
  • 动态表:记录不是固定长度的,这样存储的优点是占用的空间相对较少;缺点:频繁的更新、删除数据容易产生碎片,需要定期执行OPTIMIZE TABLE或者myisamchk-r命令来改善性能
  • 压缩表:因为每个记录是被单独压缩的,所以只有非常小的访问开支
  1. InnoDB存储引擎*
    该存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比MyISAM引擎,写的处理效率会差一些,并且会占用更多的磁盘空间以保留数据和索引。
    InnoDB存储引擎的特点:支持自动增长列,支持外键约束

  2. MEMORY存储引擎
    Memory存储引擎使用存在于内存中的内容来创建表。每个memory表只实际对应一个磁盘文件,格式是.frm。memory类型的表访问非常的快,因为它的数据是放在内存中的,并且默认使用HASH索引,但是一旦服务关闭,表中的数据就会丢失掉。
    MEMORY存储引擎的表可以选择使用BTREE索引或者HASH索引,两种不同类型的索引有其不同的使用范围

Hash索引优点:
Hash 索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 B-Tree 索引。
Hash索引缺点: 那么不精确查找呢,也很明显,因为hash算法是基于等值计算的,所以对于“like”等范围查找hash索引无效,不支持;
Memory类型的存储引擎主要用于哪些内容变化不频繁的代码表,或者作为统计操作的中间结果表,便于高效地对中间结果进行分析并得到最终的统计结果,。对存储引擎为memory的表进行更新操作要谨慎,因为数据并没有实际写入到磁盘中,所以一定要对下次重新启动服务后如何获得这些修改后的数据有所考虑。

  1. MERGE存储引擎
    Merge存储引擎是一组MyISAM表的组合,这些MyISAM表必须结构完全相同,merge表本身并没有数据,对merge类型的表可以进行查询,更新,删除操作,这些操作实际上是对内部的MyISAM表进行的。

说一下 mysql 的行锁和表锁?

说一下乐观锁和悲观锁?

mysql 问题排查都有哪些手段?

如何做 mysql 的性能优化?

Schema?

在数据库中,schema(发音 “skee-muh” 或者“skee-mah”,中文叫模式)是数据库的组织和结构,schemas 和schemata都可以作为复数形式。模式中包含了schema对象,可以是表(table)、列(column)、数据类型(data type)、视图(view)、存储过程(stored procedures)、关系(relationships)、主键(primary key)、外键(foreign key)等。数据库模式可以用一个可视化的图来表示,它显示了数据库对象及其相互之间的关系.


Redis

redis 是什么?都有哪些使用场景?

redis 有哪些功能?

redis 和 memecache 有什么区别?

redis 为什么是单线程的?

  1. Redis为什么这么快
  • 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
  • 数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
  • 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
  • 使用多路I/O复用模型,非阻塞IO;
  • 使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
  1. 多路 I/O 复用模型
    以上几点都比较好理解,下边我们针对多路 I/O 复用模型进行简单的探讨:
    多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。
    这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。

  2. 那么为什么Redis是单线程的
    我们首先要明白,上边的种种分析,都是为了营造一个Redis很快的氛围!官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了(毕竟采用多线程会有很多麻烦!)。

什么是缓存穿透?怎么解决?

redis 支持的数据类型有哪些?

  • String
    一个 key 对应一个 value
  • hash
    一个键值(key=>value)对集合。
  • List
    Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
  • Set
    Redis 的 Set 是 string 类型的无序集合。
    集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
  • zset(sorted set:有序集合)
    Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
    不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
    zset的成员是唯一的,但分数(score)却可以重复。

redis 支持的 java 客户端都有哪些?

jedis 和 redisson 有哪些区别?

怎么保证缓存和数据库数据的一致性?

redis 持久化有几种方式?

  1. RDB
    RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。


    image
  2. AOF
    AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。


    image
  3. 二者优缺点

RDB存在哪些优势呢?

  • 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。
  • 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
  • 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。
  • 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。

RDB又存在哪些劣势呢?

  • 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。
  • 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。

AOF的优势有哪些呢?

  • 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。
  • 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。
  • 如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。
  • AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。

AOF的劣势有哪些呢?

  • 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
  • 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。

二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。rdb这个就更有些 eventually consistent的意思了。

redis 怎么实现分布式锁?

  1. 如何通过Redis实现分布式锁:(非完善方法)
    SETNX key value 如果key不存在,则创建并赋值

但是此时我们获取的key是长期有效的,所以我们应该如何解决长期有效的问题呢?
EXPIRE key seconds

  1. 如何通过Redis实现分布式锁:(正确方式)

redis 分布式锁有什么缺陷?

redis 如何做内存优化?

redis 淘汰策略有哪些?

redis 常见的性能问题有哪些?该如何解决?


Mongo


Maven

maven插件开发

插件开发

Git


ES


工厂--模式,双重校验--拿到封装锁

封装锁里边有时间和重入锁
数据库分布式锁
版本、更新时间

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容