##前言
梳理了好久,总算是把面试题全部导出来了,毕竟还要上班,这次就给大家总结了一些Java开发岗位的经典面试题。

篇幅较大,阅读过程中可能会有点繁琐! 但请细细观看,文章末尾有留给大家的小惊喜!!!

千万不要错过了~ 话不多说,咱们就直接开整!

Java开发岗面试题

JavaOOP

Java的数据结构有哪些?

  • 线性表(ArrayList)
  • 链表(LinkedList)
  • 栈(Stack)
  • 队列(Queue)
  • 图(Map)
  • 树(Tree)

Java中有几种数据类型

四型八种

  • 整形:byte、short、int、long
  • 浮点型:float、double
  • 字符型:char
  • 布尔型:boolean

String str=“aaa”,与String str=new String(“aaa”)一样吗?

  • 不一样,第一个字面量声明字符串,会从常量池里面取,池中没有则创建,有则复用,后面再同样声明一个aaa时,就从池中取出复用。第二个使用new的方式创建,是不会放到常量池中的,所以也不会复用。

String、StringBuffffer 和 StringBuilder 的区别是什么?

  • String是只读字符串,它不是基本数据类型,而是一个对象,它的底层是一个final的char字符数组,一经定义,就不能增加和修改,每次对String的操作都是重新生成一个String字符串对象。
  • StringBuffffer和StringBuilder都继承了AbstractStringBulder类,2个类都是用来进行字符串操作的。
  • StringBuffffer是线程安全的,而StringBuilder是非线程安全的,所以StringBuilder效率比StringBuffffer高,StringBuffffer类的方法大多数都加了synchronized关键字。

抽象类和接口的区别是什么?

  • 抽象类

    • 需要使用abstract关键字定义,它可以有抽象方法和实例方法,抽象方法没有方法体,需要子类实现。
    • 包含抽象方法的类,一定是抽象类
    • 抽象只能被继承,不能被实例化
  • 接口

    • 接口的方法全部都是抽象方法,属性都是常量
    • 接口不能被实例化
    • 接口只能被实现,实现接口的类必须实现所有的抽象方法,除非该类是抽象类,可以选择实现部分抽象方法,剩余了让子类实现
    • 接口可以多继承

有了equals(),为什么还需要hashCode()

  • Java集合有2类,List和Set,List集合有序可重复,而Set集合无序但不可重复。Set集合保证唯一的方法,就是插入时判断对象的equals()方法,是否和已有元素重复,但是元素较多时,调用equals()方法就会很低效。所以增加了hashCode(),通过元素对象的内存地址计算出一个hash值,比较时,先比较hashCode,hashCode的效率非常高,当元素的hashCode相同时,再调用元素的equals()进行比较,这样效率就提升了。

介绍Java的强、弱、软、虚,4种引用

  • 强引用,强引用在内存不足时,宁愿发生OOM也不愿意回收它。
  • 软引用,使用SoftReference包裹引用,内存不足时,就会回收。
  • 弱引用,使用WeakReference包裹引用,只要JVM垃圾回收发现了它,就会回收。
  • 虚引用,回收机制和弱引用差不多,但是它在被回收前,会放入到ReferenceQueue队列中,并且虚引用声明时,必须传ReferenceQueue队列。因为这个机制,大部分用虚引用来做引用销毁前的处理工作。

Java创建对象有几种方式?

有4种:

  • new关键字
  • 通过反射机制
  • 通过clone克隆机制
  • 通过序列化和反序列化机制

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

例如一个对象中有一个List,浅拷贝和深拷贝效果不同。

  • 浅拷贝,只拷贝外层对象,它引用的List并不会拷贝,所以原对象和拷贝对象的List对象是同一个。
  • 深拷贝,外层对象拷贝,它所有引用的对象也拷贝,所以拷贝的对象,它引用的List对象是新的一个。

final、finalize()、finally,它们有什么区别?

  • final
    • final关键字标记的变量为常量
    • final关键字标记的类,不可继承
    • final关键字标记的方法,不可被复写
  • finalize
    • finalize()方法,在Object类上定义,用于对象被回收时,被回调,类似C++中的析构函数,可用于对对象进行销毁前的处理,但由于GC后再进行特殊处理,可能会出现内存溢出的风险,所以不推荐使用。
  • finally
    • finally用于标识代码块,和try配合使用,它在return之前执行,无论try代码块中是否发生异常,finally代码块中的代码必定会执行。

使用JDBC中,如何防止SQL注入

  • 使用PreparedStatement类,而不是使用Statement类。

Java集合、泛型

ArrayList和LinkedList的区别?

  • ArrayList底层使用数组,它的查询使用索引,所以效率高,但是增、删很慢,因为增、删都需要重排数组,例如删除中间的元素,就需要把后面的元素向前挪。
  • LinkedList底层使用链表,增、删元素只需要修改元素的前节点和后节点即可,所以效率高,但查询效率比较差。

HashMap和HashTable的区别

  • 继承关系不同
    • HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary类。
  • 对null支持不同
    • Hashtable,key和value都不能为null
    • HashMap,key可以为null,但是这样的key只能有一个,而可以多个key的value值为null
  • 线程安全
    • Hashtable是线程安全的,它的每个方法都有synchronized 关键字,所以多线程环境下可以使用它。
    • HashMap是线程不安全的,但它的效率比Hashtable高,加上大部分场景都是单线程,如果在多线程环境下,推荐使用ConcurrentHashMap,因为它使用了分段锁,并不对整个数据进行锁定。

Collection和Collections的不同

  • Collection是集合的父接口,子接口有List、Set。
  • Collections是集合类的工具类,提供了很多对集合操作的静态方法,可对集合进行搜索、排序、以及线程安全包装等。

List、Set、Map,3者的区别

  • List,有序,可重复
  • Set,无序,不可重复
  • Map,无序,键值对存储,Key不可重复,Value可重复

Array和ArrayList有什么区别?

  • Array和ArrayList都是用来存储数据,ArrayList的底层就是使用Array实现的,但它对Array的扩容和功能进行了拓展。

说说List接口和它的实现类

  • List接口是Collection接口的子接口。List接口的实现类都是有序,可重复的。List接口的实现类有ArrayList、Vector和LinkedList。
  • 实现类
    • ArrayList,底层通过数组实现,它的特点是支持索引查找,所以支持对元素的随机访问。缺点是增、删元素时,需要对数组进行复制、移动,代价比较高,所以它适合随机访问,不适合插入和删除。
    • Vector,底层和ArrayList一样是通过数组实现,但它的方法都加了同步锁,所以它可以在多线程环境下访问,避免同一时段对集合并发读写引起不一致性问题,但同步需要性能开销,所以它的效率比ArrayList低。
    • LinkedList,底层使用链表实现,很适合元素的动态插入和删除,但随机访问和遍历效率会比较低,另外它实现了Deque接口,所以拥有操作头元素、尾元素的方法,所以可以用来当做栈和队列使用。

说说Set接口和它的实现类

  • Set接口,也是Collection接口的子接口,Set接口的实现类都是不可重复的。Set接口的实现类有HashSet、TreeSet、LinkedHashSet。Set集合不可重复的特性是通过调用元素的hashCode()进行比较,如果相同,再调用equals()进行比较,都相同,就视为相同,不会添加到集合中。
  • 实现类
    • HashSet。底层通过Hash表实现,不可重复的特性是通过调用元素的hashCode()进行比较,如果相同,再调用equals()进行比较,都相同,就视为相同,不会添加到集合中。
    • TreeSet,底层通过二叉树实现,可对元素进行插入时就排序,它要求插入的元素比较实现Comparable接口,复写compareTo()函数,或者在创建TreeSet时传入自定义Comparator比较器对象,否则会在运行时抛出java.lang.ClassCastException类型的异常。
    • LinkedHashSet,底层是使用LinkedHashMap,但它只使用了Map中的Key的唯一特性,来保证不可重复。

说说Map集合和它的实现类

  • Map接口,专门用来实现键值对操作的集合类接口。它的实现类有HashMap、HashTable、TreeMap和LinkedHashMap。
  • 实现类
    • HashMap,底层使用Hash表实现,它通过元素的hashCode来确定存储的位置,所以有很快的查询速度,但它遍历时的顺序是不确定的。HashMap只允许一个key为null,但可以多个key的value为null。HashMap是非线程安全的,所以多线程环境下,对HashMap进行并发写会出现不一致性问题,可以通过Collections的synchronizedMap()方法对HashMap进行包装,让HashMap具有线程安全的能力,或者使用ConcurrentHashMap。
      • 在JDK1.8之前,HashMap底层是使用Hash表和链表实现,当发生hash冲突时,同一个位置的元素会形成链表存储,但是元素一多时,查询就会变为链表的遍历,效率比较低。
      • 在JDK1.8时,HashMap底层就变成Hash表和链表\红黑树实现,当链表中的元素个数超过8时,链表转换为红黑树,避免遍历,优化了查询效率。
    • HashTable,底层和JDK1.7的HashMap类似,但它的key和value都不能为null,而且Hashtable是线程安全的,它的每个方法都有synchronized 关键字,所以多线程环境下可以使用它。
    • TreeMap,底层是通过二叉树实现,它能在元素添加时,进行排序,它要求元素必须实现Comparable接口,复写compareTo()函数,或者在创建TreeMap时传入自定义Comparator比较器对象,否则会在运行时抛出java.lang.ClassCastException类型的异常。
    • LinkedHashMap,它是HashMap的一个子类,保存了插入顺序,而其他Map实现类是无序的。

什么是泛型?什么是泛型擦除?

  • 泛型可以对类型进行抽象,可以让类型支持不同类型而重用,例如容器类ArrayList,通过泛型,ArrayList可以存储不同的元素,并且泛型后的Array元素是具体类型而不是Object,避免了类型转换的麻烦,而且编译时会报错,避免了类型转换可能发生的类型转换错误。
  • 泛型擦除,因为Java的泛型是通过编译时实现的,生成的Java字节码中是不存在泛型信息的,所以泛型信息,在编译器编译时,会将泛型信息去除,这个过程就是泛型擦除。所以List上附加的泛型信息,在JVM来说是不可见的,在它眼里都是Object类型。

Java异常面试题

Java异常分为哪几种?

  • 编译时异常
  • 运行时异常

介绍一下Java的异常处理机制

  • 捕获异常,使用try-catch-finally
  • 异常抛出,使用throws关键字

如果自定义一个异常

  • 继承一个异常类,运行时异常继承RuntimeException,编译时异常继承Exception。

try-catch-finally,try中有return,finally还执行吗?

  • 执行,finally的执行在return之前,不管try中有没有异常,都会执行。另外如果finally中有return,则会在try的return之前执行,那么try中的return就执行不到了。

Excption与Error的关系

  • Excption与Error都是Throwable的子类。
  • Excption有运行时异常和编译时异常,他们都是可以捕获和处理。
    • 编译时异常常见有:IOException、FileNotFoundException、SQLException,必须在代码中处理,否则编译会报错。
    • 运行时异常常见有:ClassCastException、IndexOutOfBoundsException、NullPointerException
  • Error,和运行时异常一样,编译器也不会对错误进行检查。当资源不足、约束失败、或是其它程序无法继续运行的条件发生时,就产生错误。程序本身无法修复这些错误的。常见子类有OutOfMemoryError

Java中的IO和NIO面试题

Java的IO流分为几种

  • 按流的流向分,可以分为输入流和输出流
  • 按操作的单元分,可以分为字节流和字符流
  • 按照流的角色分,可以分为节点流和处理流

Java IO流中40多个类,都是从以下4个抽象基类中派生出来的:

  • InputStream\Reader,所有输入流的基类,InputStream为字符输入流,Reader为字符输入流。
  • OutputStream\Writer,所有输出流的基类,OutputStream为字节输出流,Writer为字符输出流。

Java中IO和NIO的区别?

  • NIO被称为New IO,在JDK1.4中引入,NIO和IO有相同的目的和作用,但实现方式不同。NIO主要用到的是块,而IO是字节Byte,所以NIO的效率要比IO高很多。Java提供了2套NIO,一套针对文件,另一套针对网络编程。

常用io类有哪些?

  • 字节
    • FileInputSteam、FileOutputStream
    • BufferInputStream、BufferedOutputSream
  • 字符
    • FileReader、FileWriter
    • BufferReader、BufferedWriter
  • 对象序列化
    • ObjectInputStream、ObjectOutputSream

什么是Java NIO

  • NIO 主要有三大核心部分: Channel(通道), Buffer(缓冲区), Selector。
  • 传统 IO 基于字节流和字符流进行操作, 而 NIO 基于 Channel 和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。 Selector(选择区)用于监听多个通道的事件(比 如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。
  • NIO 和传统 IO 之间第一个最大的区别是, IO 是面向流的, NIO 是 面向缓冲区的。

什么是NIO的Channel

  • Channel,一般翻译为通道。 Channel 和 IO 中的 Stream(流)是差不多一个等级的。 只不过 Stream 是单向的,譬如: InputStream, OutputStream, 而 Channel 是双向的,既可以用来进行读操作,又可以用来进行写操作。
  • NIO 中的 Channel 的主要实现类
    • FileChannel(文件操作)
    • DatagramChannel(UDP操作)
    • SocketChannel(TCP客户端)
    • ServerSocketChannel(TCP服务端)

什么是NIO的Buffer

  • Buffer,故名思意, 缓冲区,实际上是一个容器,是一个连续数组。 Channel 提供从文件、网络读取数据的渠道,但是读取或写入的数据 都必须经由 Buffer。
  • 从一个客户端向服务端发送数据,然后服务端接收数据的过程。客户端发送数据时,必须先将数据存入 Buffer 中,然后将 Buffer 中的内容写入通道。服务端这边接收数据必须通过 Channel 将数据读入到 Buffer 中,然后再从 Buffer 中取出数据来处理。
  • 在 NIO 中, Buffer 是一个顶层父类,它是一个抽象类,常用的 Buffer 的子类有
    • ByteBuffer
    • ShortBuffer
    • IntBuffer
    • LongBuffer
    • FloatBuffer
    • DoubleBuffer
    • CharBuffer

什么是NIO的Selector

  • Selector 类是 NIO 的核心类, Selector 能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行 相应的响应处理。这样一来,只是用一个单线程就可以管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时, 才会调用函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,并且避免了多线程之间 的上下文切换导致的开销。

Java反射面试题

Java反射创建对象效率高,还是new创建对象的效率高

  • 通过new创建对象的效率比较高。通过反射时,先找查找类资源,使用类加载器创建,过程比较繁琐,所以效率较低

Java反射的作用

  • 反射机制是在运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,都能够调用它的任意一个方法。在java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。

哪里会用到反射机制

例如:加载MySQL的驱动类,如Hibernate、MyBatis等框架中会使用。

//加载MySQL的驱动类
Class.forName('com.mysql.jdbc.Driver.class');
复制代码

反射机制的优缺点

  • 优点
    • 能够运行时动态获取类的实例,提高灵活性
    • 与动态编译结合
  • 缺点
    • 使用反射性能较低,需要解析字节码,将内存中的对象进行解析
    • 相对不安全,破坏了封装性(因为通过反射可以获得私有方法和属性)
  • 解决方案
    • 通过setAccessible(true),关闭JDK的安全检查来提升反射速度
    • 第一次反射后,会有缓存,下次反射会快很多
    • ReflflectASM工具类,通过字节码生成的方式加快反射速度

Java注解面试题

注解是什么?

  • Annotation(注解)是 Java 提供的一种对元程序中元素关联信息和元数据(metadata)的途径和方法。 Annatation(注解)是一个接口,程 序可以通过反射来获取指定程序中元素的 Annotation对象,然后通过该 Annotation 对象来获取注解中的元数据信息。

4种标准元注解是哪四种?

  • @Target,修饰的对象范围
    • @Target说明了Annotation所修饰的对象范围: Annotation可被用于 packages、types(类、接口、枚举、Annotation 类型)、类型成员 (方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch 参数)。在 Annotation 类型的声明中使用了 target 可更加明晰其修饰的目标
  • @Retention,定义被保留的时间长短
    • Retention 定义了该 Annotation 被保留的时间长短:表示需要在什么级别保存注解信息,用于描述注解的生命周期(即:被描述的注解在 什么范围内有效),取值(RetentionPoicy)由:
      • SOURCE:在源文件中有效(即源文件保留)
      • CLASS:在 class 文件中有效(即 class 保留)
      • RUNTIME:在运行时有效(即运行时保留)
  • @Inherited 阐述了某个被标注的类型是被继承的
    • @Inherited 元注解是一个标记注解,@Inherited 阐述了某个被标注的类型是被继承的。如果一 个使用了@Inherited 修饰的 annotation 类型被用于一个 class,则这个 annotation 将被用于该class 的子类。

Java多线程、并发面试题

Java中实现多线程有几种方法

一共有4种方式

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口,通过FutureTask包装器,来创建Thread线程
  • 使用ExecutorService、Callable、Future实现有返回结果的多线程(也就是使用了ExecutorService,管理前面的三种方式)

如何停止一个正在运行的线程

  • 使用退出标志,使线程正常退出,也就是当run()方法完成后线程终止
  • 使用stop方法强行终止,但是不推荐这个方法,可能会导致线程操作的数据不一致
  • 使用interrupt方法中断线程,并捕获InterruptedException异常

volatile是什么?可以保证有序性吗?

  • 共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义
    • 保证不同线程对这个共享变量进行操作时,有可见性,就是其中一个线程对该变量值进行修改,其他线程是马上可见的,volatile关键字会强制将修改的值同步到主内存。
    • 禁止指令重排,禁止编译器优化代码顺序,避免在单例Double Check中导致多次初始化,保证有有序性。
  • 注意,volatile不能保证原子性。

Thread 类中的start() 和 run() 方法有什么区别?

  • start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

Java中synchronized 和 ReentrantLock 有什么不同?

  • 相似点

    • 这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的。
  • 区别

    • 对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。
    • 而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配 合try/finally语句块来完成。
    • Synchronized进过编译,会在同步块的前后分别形成monitorenter和monitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计数器加1,相应的,在执行monitorexit指令时会将锁计数器就减1,当计数器为0时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止 。
  • ReentrantLock特性

    • 等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。
    • 公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁, ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性 能不是很好。
    • 锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象 。

SynchronizedMap和ConcurrentHashMap有什么区别?

  • SynchronizedMap()和Hashtable一样,实现上在调用map所有方法时,都对整个map进行同步。而 ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。所以,只要有一个线程访问 map,其他线程就无法进入map,而如果一个线程在访问ConcurrentHashMap某个桶时,其他线程, 仍然可以对map执行某些操作。
  • 所以,ConcurrentHashMap在性能以及安全性方面,明显比Collections.synchronizedMap()更加有优势。同时,同步操作精确控制到桶,这样,即使在遍历map时,如果其他线程试图对map进行数据修改,也不会抛出ConcurrentModificationException 。

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

两个方法都可以向线程池提交任务。

  • execute()方法,它的返回类型是void,任务执行完后,没有返回值结果,它定义在Executor接口中。
  • submit()方法,它可以返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了Executor接口。

说一说自己对于 synchronized 关键字的了解

  • synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能 有一个线程执行。
  • 在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来 实现的,Java 的线程是映射到操作系统的原生线程之上的。
  • 如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的 转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的synchronized 效率低的原因。
  • 在 Java 6 之后 Java 官方对从 JVM 层面对synchronized 较大优化,所以现在的 synchronized 锁效率也优化得很不错了。JDK1.6对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。

volatile关键字的作用?

  • 一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
  • 禁止进行指令重排序。
    • volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量, 只有当前线程可以访问该变量,其他线程被阻塞住。
    • volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。
    • volatile仅能实现变量的修改可见性,并不能保证原子性;synchronized则可以保证变量的修改可见性和原子性。
    • volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
    • volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。

简述一下你对线程池的理解

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程生命周期

  • 新建状态(NEW)
    • 当程序使用 new 关键字创建了一个线程之后,该线程就处于新建状态,此时仅由 JVM 为其分配内存,并初始化其成员变量的值。
  • 就绪状态(RUNNABLE)
    • 当线程对象调用了 start()方法之后,该线程处于就绪状态。 Java 虚拟机会为其创建方法调用栈和程序计数器,等待调度运行。
  • 运行状态(RUNNING)
    • 如果处于就绪状态的线程获得了 CPU,开始执行 run()方法的线程执行体,则该线程处于运行状态。
  • 阻塞状态(BLOCKED)
    • 阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才 有机会再次获得 cpu timeslice 转到运行(running)状态。
      • 阻塞的情况分三种
        • 等待阻塞(o.wait->等待对列),运行(running)的线程执行 o.wait()方法, JVM 会把该线程放入等待队列(waitting queue)中。
        • 同步阻塞(lock->锁池),运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池(lock pool)中。
        • 其他阻塞(sleep/join),运行(running)的线程执行 Thread.sleep(long ms)或 t.join()方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep()状态 超时、 join()等待线程终止或者超时、或者 I/O处理完毕时,线程重新转入可运行(runnable)状态。
  • 线程死亡(DEAD)
    • 线程会以下面三种方式结束,结束后就是死亡状态。
      • run()或 call()方法执行完成,线程正常结束。
      • 线程抛出一个未捕获的 Exception 或 Error,线程异常结束。
      • 调用stop停止。直接调用该线程的 stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用。

什么是乐观锁?

  • 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新 的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复,读 比较 写的操作。
  • Java中的乐观锁基本都是通过 CAS 操作实现的, CAS 是一种更新的原子操作, 比较当前值跟传入值是否一样,一样则更新,否则失败。

什么是悲观锁?

  • 悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上 锁,这样别人想读写这个数据就会 block 直到拿到锁。Java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试CAS乐观锁去获取锁, 获取不到,才会转换为悲观锁,如RetreenLock。

什么是可重入锁(递归锁)

  • 本文里面讲的是广义上的可重入锁,而不是单指 JAVA 下的 ReentrantLock。 可重入锁,也叫 做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受 影响。在 JAVA 环境下 ReentrantLock 和 synchronized 都是 可重入锁。

公平锁与非公平锁

  • 公平锁(Fair)
    • 加锁前检查是否有排队等待的线程,优先排队等待的线程,先到先得。
  • 非公平锁(Nonfair)
    • 加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待
  • 对比
    • 非公平锁性能比公平锁高 5~10 倍,因为公平锁需要在多核的情况下维护一个队列。
    • Java 中的 synchronized 是非公平锁, ReentrantLock 默认的 lock()方法采用的是非公平锁。

在 Java 中 Executor 和 Executors 的区别?

  • Executors 工具类的不同方法按照我们的需求创建了不同的线程池,来满足业务的需求。
  • Executor 接口对象能执行我们的线程任务。
  • ExecutorService 接口继承了 Executor 接口并进行了扩展,提供了更多的方法我们能获得任务执行的状态并且可以获取任务的返回值。 使用 ThreadPoolExecutor 可以创建自定义线程池。
  • Future 表示异步计算的结果,他提供了检查计算是否完成的方法,以等待计算的 完成,并可以使用 get()方法获取计算的结果。

MySQL面试题

什么是数据库引擎?

  • 数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供 不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获得特定的功能。现在许多不同的数据库管理系统都支持多 种不同的数据引擎。
  • 存储引擎主要有: 1. MyIsam , 2. InnoDB, 3. Memory, 4. Archive, 5. Federated 。

InnoDB底层数据结构是什么?适用什么场景?

  • InnoDB底层数据结构为B+树,B+树的每个节点对应InnoDB的一个page,page的大小是固定的,一般设为16k,其中非叶子节点只有键值,叶子节点包含数据。
  • 适用场景
    • 经常更新的表,适合处理多重并发的更新请求。
    • 支持事务。
    • 可以从灾难中恢复(通过bin-log日志等)
    • 外键约束(只有它支持外键约束)
    • 支持自动增加列属性(auto_increment)

MyIASM的优点和缺点是什么?

  • MyIASM是 MySQL默认的引擎
  • 优点
    • ISAM 执行读取操作的速度很快,而且不占用大量的内存和存储资源。
  • 缺点
    • 不支持事务。
    • 表级锁,不支持行级锁和外键,因此当INSERT(插入)或UPDATE(更新)数据 时即写操作需要锁定整个表,效率会低一些。

InnoDB与MyISAM的区别

  • InnoDB支持事务,MyISAM不支持,InnoDB将每条SQL语句都默认添加事务,自动提交,这样会影响效率,所以最好将多条SQL语句放在begin和commit之间,组成一个事务。
  • InnoDB支持外键,MyISAM不支持,如果将一个包含外键的InnoDB表转为MyISAM会失败。
  • InnoDB是聚集索引,数据文件和索引是绑在一起的,必须有主键,通过主键查询效率会很高。MyISAM是非聚集索引,数据文件和索引是分离的。
  • InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描,而MyISAM用一个变量保存,执行上面这个语句时,只要读出该变量即可,速度很快。
  • InnoDB不支持全文索引,而MyISAM支持,所以MyISAM的查询效率比较高。

什么是索引?有几种索引?索引越多越好吗?

  • 索引是加快检索表中数据的方法,数据库的索引类似书籍的索引,在书籍中,允许用户不必翻阅整本书就能迅速的找到需要的信息,在数据库中,索引也允许数据库迅速地找到表中的数据,而不必扫描整个数据库。
  • MySQL中有4种不同的索引
    • 主键索引
    • 唯一索引
    • 普通索引
    • 全文索引
  • 索引不是越多越好,创建索引也需要消耗资源,一是增加了数据库的存储空间,二是插入和删除表数据,都要花较多的时间维护索引。

常见索引原则

  • 字段是唯一的,建立唯一索引,可以更快速通过索引来确定某条记录。
  • 经常需要排序、分组、联合操作的字段建立索引。
  • 为常作为查询条件的字段建立索引。
  • 删除不再使用或很少使用的索引。
  • 索引列不能参加计算,带函数的查询,不参与索引。
  • 最左前缀匹配原则。

数据库的三范式是什么?

  • 第一范式,列不可再分
  • 第二范式,行有唯一区分的字段,主键约束
  • 第三范式,表的非主属性不能依赖与其他表的非主属性外键约束

什么是数据库事务?

  • 事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列操作, 这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行。
  • 事务是一个不可分割的工作逻辑单元,事务必须具备以下四个属性,简称 ACID 属性
    • 原子性(Atomicity)
      • 事务是一个完整的操作。事务的各步操作是不可分的(原子的);要么都执行,要么都不执 行。
    • 一致性(Consistency)
      • 当事务完成时,数据必须处于一致状态。
    • 隔离性(Isolation)
      • 对数据进行修改的所有并发事务是彼此隔离的, 这表明事务必须是独立的,它不应以任何方 式依赖于或影响其他事务。
    • 永久性(Durability)
      • 事务完成后,它对数据库的修改被永久保持,事务日志能够保持事务的永久性。

SQL优化

  • 查询语句中不要使用select *
  • 尽量减少子查询,使用关联查询(left join、right join、inner join)替代
  • 减少使用IN或者NOT IN ,使用exists,not exists或者关联查询语句替代
  • or 的查询尽量用 union 或者 union all 代替(在确认没有重复数据或者不用剔除重复数据时,union all会更好)
  • 应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
  • 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num = 0

drop、delete与truncate的区别

  • delete和truncate只删除表的数据不删除表的结构。
  • delete删除记录,不删除表结构,delete语句可以加where,删除操作会记录在日志,可以回滚,删除时,会激活触发器。
  • truncate删除记录,不删除表结构,truncate语句不可以加where,删除操作不记录在日志,不能回滚,不会激活触发器。
  • drop会删除记录和结构,不会激活触发器。
  • 删除速度来讲,drop > truncate > delete

什么是内联接、左外联接、右外联接?

  • 内联接(Inner Join):匹配2张表中相关联的记录。(2张表中没有关联的部分抛弃)
  • 左外联接(Left Outer Join):除了匹配2张表中相关联的记录外,还会匹配左表中剩余的记录,右表中未匹配到的字段用NULL表示。(以左边记录匹配,如果右表中没有记录与其匹配,字段值为NULL)
  • 右外联接(Right Outer Join):除了匹配2张表中相关联的记录外,还会匹配右表中剩余的记录,左表中未匹配到的字段用NULL表示。(以右边记录匹配,如果左表中没有记录与其匹配,字段值为NULL)

并发事务带来哪些问题?

  • 脏读(Dirty read),当一个事务读取了数据,并且修改了,但还未提交到数据库中,另外一个事务也读取了数据,并且使用了该数据,这时另外一个数据读取到的数据就是“脏数据”,根据“脏数据”所做的处理可能是不正确的。
  • 丢失修改(Lost to modify),当一个事务读取了数据,另外一个事务也读取了数据,在第一个事务修改了数据后,第二个事务也修改了数据,这样第一个事务的修改则被丢失,因为为“丢失修”,例如事务1读取了数据A=20,事务2也读取了A=20,事务1修改A=A1,事务2也修改A=A-1,最终结果为A=19,事务1的修改被丢失了。
  • 不可重复读(Unrepeatableread),指一个事务多次读取1个数据,在这个事务还未结束时,另外一个事务也访问该数据,如果第二个事务修改了数据,导致第一个事务的多次读取的数据结果可能是不一样的,因此成为不可重复读。
  • 幻读(Phantom read),幻读和不可重复读类似,它发生在一个事务读取了几行数据,接着另外一个事务插入了一些数据,在随后的查询中,第一个事务发现多了一些原本不存在的数据,就像产生了幻觉一样,所以称为幻读。

不可重复读和幻读的区别

  • 不可重复读的重点是修改,例如多次读取一条记录,发现记录的某一列的值被修改。
  • 幻读的重点是新增或减少,例如多次读取,发现记录增多或减少了。

事务隔离级别有哪些?MySQL的默认隔离级别是?

SQL 标准定义了四个隔离级别

  • READ-UNCOMMITTED(读取未提交):最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。

  • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。

  • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被事务本身自己所修改,可以阻止脏读和不可重复 读,但幻读仍有可能发生。

  • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

  • MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读),我们可以通过 SELECT @@tx_isolation; 命令来查看。

但要注意,MySQL InnoDB在 REPEATABLE-READ(可重读)隔离级别下,使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,所以MySQL默认的的隔离级别,REPEATABLE-READ级别也达到了SERIALIZABLE(可串行化)级别的隔离要求。因为级别越高,事务请求的锁越多,所以大部分的数据库隔离级别都是READ-COMMITTED(读取已提交)。

大表如何优化?

  • 限定查询数据的范围,例如查询订单历史时,控制查询一个月内的订单。
  • 读、写分离,主库复写写,从库负责读。
  • 垂直分区,例如用户表既有用户的登录信息,也有用户的基本信息,可以进行垂直拆分,把用户表拆分为2张表,就是把数据列拆分到多张表。
  • 水平分区,保持表结构不变,通过某种策略将数据分散到不同的表或库中,例如根据年、月,一年一张表,一月一张表,水平分区可以支持非常大的数据量。水平分区的分表只能解决单张表的存储数据量过大问题,但由于数据还是在一台机器上,对于提供并发能力并没有什么意义,所以水平拆分最好分库。

分库分表后,主键id如何处理

分库分表后,每个表的id都是从1开始累加,这样是不对的,我们需要一个全局唯一id来支持。

  • UUID,太长了,并且无序,查询效率低,比较用于生成文件名等。
  • 数据自增Id,每台数据库分别设置不同的步长,生成不重复的ID,这种方式生成ID有序,但是需要独立部署数据库实例,成本高,还有性能瓶颈。
  • 利用Redis生成id,性能好,灵活方便,不依赖于数据库,但引入了新的组件造成系统更复杂,可用性降低,编码更加复杂,增加了系统成本。
  • Twitter的snowflake雪花算法。
  • 美团的Leaf分布式ID生成系统,Leaf 是美团开源的分布式ID生成器,能保证全局唯一性、趋势递增、单调递增、信息安全,但也依赖关系数据库,Zookeeper等中间件。

MySQL中有哪几种锁?

  • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
  • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
  • 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

NOW() 和 CURRENT_DATE() 有什么区别?

  • NOW() 命令用于显示当前年份,月份,日期,小时,分钟和秒。
  • CURRENT_DATE() 仅显示当前年份,月份和日期。

锁的优化策略

  • 读写分离
  • 分段加锁
  • 减少锁持有的时间
  • 多个线程尽量以相同的顺序去获取资源不能将锁的粒度过于细化,不然可能会出现线程的加锁和释放次数过多,反而效率不如一次加一把大锁。

索引的底层实现原理和优化

  • 底层是B+树,经过优化的 B+树。
  • 主要是在所有的叶子结点中增加了指向下一个叶子节点的指针,因此 InnoDB 建议为大部分表使用默认自增的主键作为主索引。

索引的目的是什么?

  • 快速访问数据表中的特定信息,提高检索速度。
  • 加速表和表之间的连接,使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。
  • 创建唯一性索引,保证数据库表中每一行数据的唯一性。

索引对数据库系统的负面影响是什么?

  • 创建索引和维护索引需要耗费时间,这个时间随着数据量的增加而增加;索引需要占用物理空间,不光是表需要占用数据空间,每个索引也需要占用物理空间;当对表进行增、删、改、的时候索引也要动态维护,这样就降低了数据的维护速度。

为数据表建立索引的原则有哪些?

  • 在最频繁使用的、用以缩小查询范围的字段上建立索引。
  • 在频繁使用的、需要排序的字段上建立索引。

什么情况下不宜建立索引?

  • 对于查询中很少涉及的列或者重复值比较多的列,不宜建立索引。
  • 对于一些特殊的数据类型,不宜建立索引,比如文本字段(text)等。

什么情况索引会失效

  • 以“%”开头的 LIKE 语句,模糊匹配
  • OR 语句前后没有同时使用索引
  • 数据类型出现隐式转化(如 varchar 不加单引号的话可能会自动转换为 int 型)

实践中如何优化 MySQL

  • SQL 语句及索引的优化
  • 数据库表结构的优化
  • 系统配置的优化
  • 硬件的优化

优化数据库的方法

  • 选取最适用的字段属性,尽可能减少定义字段宽度,尽量把字段设置 NOT NULL,例如’省份’、’性别’最好使用 ENUM 枚举
  • 使用连接(JOIN)来代替子查询
  • 适用联合(UNION)来代替手动创建的临时表
  • 事务处理
  • 锁定表、优化事务处理
  • 适用外键,优化锁定表
  • 建立索引
  • 优化查询语句

简单描述 MySQL 中,索引,主键,唯一索引,联合索引的区别,对数据库的性能有什么影响(从读写两方面)

  • 索引是一种特殊的文件(InnoDB 数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。
  • 普通索引(由关键字 KEY 或 INDEX 定义的索引)的唯一任务是加快对数据的访问速度。
  • 普通索引允许被索引的数据列包含重复的值。如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该用 关键字 UNIQUE 把它定义为一个唯一索引。也就是说,唯一索引可以保证数据记录的唯一性。
  • 主键,是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条记录,使用关键字 PRIMARY KEY 来创建。
  • 索引可以覆盖多个数据列,如像 INDEX(columnA, columnB)索引,这就是联合索引。
  • 索引可以极大的提高数据的查询速度,但是会降低插入、删除、更新表的速度,因为在执行这些写操作时,还要操作索引文件。

SQL 注入漏洞产生的原因?如何防止?

  • SQL 注入产生的原因:程序开发过程中不注意规范书写 sql 语句和对特殊字符进行过滤,导致客户端可以通过全局变量 POST 和 GET 提交 一些 sql 语句正常执行。

  • 防止 SQL 注入的方式

    • 开启配置文件中的 magic_quotes_gpc 和 magic_quotes_runtime 设置 执行 sql 语句时使用 addslashes 进行 sql 语句转换
    • Sql 语句书写尽量不要省略双引号和单引号。
    • 过滤掉 sql 语句中的一些关键词:update、insert、delete、select、 * 。
    • 提高数据库表和字段的命名技巧,对一些重要的字段根据程序的特点命名,取不易被猜到的。

Redis面试题

Redis 的数据类型?

  • Redis 支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及 zsetsorted set:有序集合)。

使用 Redis 有哪些好处?

  • 速度快,因为数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是 O1)。
  • 支持丰富数据类型,支持 string,list,set,Zset,hash 等。
  • 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行。
  • 丰富的特性:可用于缓存,消息,按 key 设置过期时间,过期后将会自动删除。

Redis 相比 Memcached 有哪些优势?

  • Memcached 所有的值均是简单的字符串,redis 作为其替代者,支持更为丰富的数据类。
  • Redis 的速度比 Memcached 快很多。
  • Redis 可以持久化其数据。

Memcache 与 Redis 的区别都有哪些?

  • 存储方式:Memecache 把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis 有部份存在硬盘上,这样能保证数据 的持久性。
  • 数据支持类型:Memcache 对数据类型支持相对简单。 Redis 有复杂的数据类型。
  • 使用底层模型不同:它们之间底层实现方式以及与客户端之间通信的应用协议不一样。Redis 直接自己构建了 VM 机制 ,因为一般的系 统调用系统函数的话,会浪费一定的时间去移动和请求。

Redis 是单进程单线程的?

  • Redis 是单进程单线程的,redis 利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销。

Redis持久化机制

  • RDB是Redis默认的持久化方式。按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。即Snapshot快照存储,对 应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。( 快照可以是其所表示的数据的一个副本,也可以是数据 的一个复制品)

  • AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启是会通过重新执行文件中保 存的写命令来在内存中重建整个数据库的内容。当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。

什么是缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级,怎么解决

缓存雪崩

  • 定义

    • 我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓 存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一 系列连锁反应,造成整个系统崩溃。
  • 解决办法

    • 大多数系统设计者考虑用加锁( 最多的解决方案)或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效 时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开。

缓存穿透

  • 定义

    • 缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再 查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。
  • 解决办法

    • 最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从 而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故 障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次 到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。

缓存预热

  • 定义

    • 缓存预热这个应该是一个比较常见的概念,相信很多小伙伴都应该可以很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加 载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
  • 解决办法

    • 直接写个缓存刷新页面,上线时手工操作下。
    • 数据量不大,可以在项目启动的时候自动进行加载。
    • 定时刷新缓存。

缓存更新

  • 定义

    • 除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种。
  • 解决办法

    • 定时去清理过期的缓存。
    • )当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数 据并更新缓存。
  • 优缺点

    • 两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的。
    • 第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较 复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡

缓存降级

  • 定义

    • 当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
    • 降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
  • 参考日志级别设置预案

    • 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级。
    • )警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警。
    • 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降 级或者人工降级。
    • 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一 起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查 询,而是直接返回默认值给用户。

单线程的redis为什么这么快

  • 纯内存操作。
  • 单线程操作,避免了频繁的上下文切换。
  • 采用了非阻塞I/O多路复用机制。

redis的数据类型,以及每种数据类型的使用场景

  • String。这个其实没啥好说的,最常规的set/get操作,value可以是String也可以是数字。一般做一些复杂的计数功能的缓存。
  • hash。这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这种数据结构存储用户信息, 以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果。
  • list。使用List的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用lrange命令,做基于redis的分页功能,性能极佳,用户 体验好。本人还用一个场景,很合适—取行情信息。就也是个生产者和消费者的场景。list可以很好的完成排队,先进先出的原则。
  • set。因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。为什么不用JVM自带的Set进行去重?因为我们的系统一般都是集群部 署,使用JVM自带的Set,比较麻烦,难道为了一个做一个全局去重,再起一个公共服务,太麻烦了。 另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。
  • sorted set。sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用,取 TOP N 操作

Redis 的持久化机制是什么?各自的优缺点?

Redis 提供两种持久化机制 RDB 和 AOF 机制

  • RDB (Redis DataBase) 持久化方式: 是指用数据集快照的方式,半持久化模式记录 redis 数据库的所有键值对,在某个时间点将数据写入一 个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。

    • 优点
      • 只有一个文件 dump.rdb,方便持久化。
      • 容灾性好,一个文件可以保存到安全的磁盘。
      • 性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO最大化。使用单独子进程来进行持久化,主进程不会进行任 何 IO 操作,保证了 redis的高性能) 4.相对于数据集大时,比 AOF 的启动效率更高。
    • 缺点
      • 数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候。
  • AOF(Append-only file)持久化方式: 是指所有的命令行记录以 redis 命令请求协议的格式完全持久化存储保存为 aof 文件。

    • 优点
      • 数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次命令操作就记录到 aof 文件中一次。
      • 通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof工具解决数据一致性问题。
      • AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作 的 flushall)
    • 缺点
      • AOF 文件比 RDB 文件大,且恢复速度慢。
      • 数据集大的时候,比 rdb 启动效率低。

怎么理解 Redis 事务?

  • 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请 求所打断。
  • 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

Spring面试题

什么是 Spring IOC 容器?

  • Spring 框架的核心是 Spring 容器。容器创建对象,将它们装配在一起,配置它们并管理它们的完整生命周期。Spring 容器使用依赖注入来 管理组成应用程序的组件。容器通过读取提供的配置元数据来接收对象进行实例化,配置和组装的指令。该元数据可以通过 XML,Java 注 解或 Java 代码提供。

什么是依赖注入?

  • 在依赖注入中,您不必创建对象,但必须描述如何创建它们。您不是直接在代码中将组件和服务连接在一起,而是描述配置文件中哪些组件 需要哪些服务。由 IoC容器将它们装配在一起。

可以通过多少种方式完成依赖注入?

  • 通常,依赖注入可以通过三种方式完成
    • 构造器注入
    • setter注入
    • 接口注入

IOC有什么好处?

  • 依赖关系的松耦合
  • 支持延时初始化和立即初始化
  • 方便测试框架中进行测试用例的测试

Spring的Scope有几种?

  • Singleton,单例。
  • Prototype,多例,每次调用都重新生成一个实例注入。
  • Request,Web项目中,给每个Http Request都创建一个实例。
  • Session,Web项目中,给每个Http Session都创建一个实例。
  • GlobalSession,这种只在portal应用中有用,给每个Global Http Session都创建一个实例。

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

  • Xml
    • byName,查找类中的setter方法,去掉set前缀,首字母转小写,用这个名称去容器中找以这个名称为id的Bean实例。
    • byType,查找类中的setter方法,根据形参的类型,去容器中查找对应的实例,必须保证类型只有1个实例,否则spring不知道注入哪个,会报错。
  • 注解
    • @Autowired,可标识在构造方法、成员方法、形参、成员变量上,根据变量类型查询容器实例注入,必须确保类实例的唯一性。
    • @Autowired + @Qualifier,如果实例为多个,则添加@Qualifier注解,指定确定id的类实例进行注入。
    • @Resource,相当于上面@Autowired + @Qualifier组合为一个注解的方式。

Spring 事务实现方式有哪些?

  • 编程式事务,通过拿到DataSourceTransactionManager进行手动开启事物,提交事物,和事物回滚操作。
  • 声明式事物
    • 通过声明事务代理工厂 TransactionProxyFactoryBean 的Bean指定事务管理器bean和需要代理的目标bean,这样spring容器中就有了一个目标bean的代理bean,使用这个bean可以对目标bean实现事物的增强。
    • @Transactional注解,在spring xml配置文件中配置启动事物注解,可以在指定的函数中添加事物注解。
    • 基于Aspectj AOP配置事务,设置切入点,指定事物属性,事物属性指定事物管理器。

说一下Spring的事务隔离级别

Spring事务隔离级别比数据库事务隔离级别多一个default。

  • ISOLATION_DEFAULT(默认),这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。
  • ISOLATION_READ_UNCOMMITTED (读未提交),这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。
  • ISOLATION_READ_COMMITTED (读已提交),保证一个事务修改的数据提交后才能被另外一个事务读取,另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻读。
  • ISOLATION_REPEATABLE_READ (可重复读),这种事务隔离级别可以防止脏读、不可重复读,但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读。
  • ISOLATION_SERIALIZABLE(串行化)这是花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行。除了防止脏读、不可重复读外,还避免了幻读。

Spring事务传播属性

  • REQUIRED(默认属性),如果有当前事务,则加入这个事务,如果没有,就新建一个事务。
  • SUPPORTS,如果当前没有事务,则以非事务方式执行。
  • MANDATORY,如果当前有事务,则加入这个事务,没有事务,则抛出异常。
  • REQUIRES_NEW,新建事务,如果当前有事务,则将事务挂起。
  • NOT_SUPPORTED,以非事务方式执行,如果当前有事务,则将事务挂起。
  • NEVER,以非事务方式执行,如果当前有事务,就抛出异常。
  • NESTED,嵌套,新增SavePoint保存点,与当前事务同步提交和回滚,嵌套事务有一个非常重要的概念,就是内层事务依赖于外层事务,外层事务失败时,会回滚内层事务所做的操作,而内层事务操作失败,不会导致外层事务的回滚。

什么是 AOP?

  • AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角. 在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)。

AOP 有哪些实现方式?

实现AOP的技术,主要分为两大类。

  • 静态代理(指使用AOP框架提供的命令进行编译,从而在编译时,生成AOP代理类,因此也称为编译时增强)
    • 编译时织入(特殊编译器实现)
    • 类加载时织入(特殊的类加载器实现)
  • 动态代理(在运行时在内存中生成AOP代理类,因此也成为运行时增强)
    • JDK动态代理
    • CGLIB

Spring AOP 和 AspectJ AOP 有什么区别?

  • Spring AOP基于动态代理实现,AspectJ基于静态代理实现。Spring AOP只支持方法级别的切入,而AspectJ AOP提供完整的AOP支持,例如支持属性级别的切入。

描述一下 DispatcherServlet 的工作流程

  • 当服务器收到请求后,被DispatcherServlet捕获。
  • DispatcherServlet根据URL查询HandlerMapping,找到对应的处理器Handler,以及拦截器,封装成HandlerExecutionChain。
  • DispatcherServlet再根据Handler找到对应的HandlerAdapter,找到后,调用拦截器的前置拦截。
  • 开始Handlerc处理器的处理方法,处理完毕,通过HandlerAdapter转换为统一的ModelAndView,调用拦截器的后置拦截。
  • 根据返回的ModelAndView,通过ViewResolver,找到合适的View页面对象,调用View页面对象的render方法,传入model,进行渲染处理,返回页面数据给客户端。

Spring Boot面试题

什么是 Spring Boot?

  • Spring Boot建立在已有的Spring框架之上,Spring Boot提供了很多开箱即用的Starter,按约定大于配置,提供了很多默认配置,减少每次开启一个项目时需要一堆大同小异的配置文件。

为什么要用SpringBoot?

SpringBoot有以下优点

  • 独立运行
    • Spring Boot内嵌了各种servlet容器,例如Tomcat、Jetty等,现在不再需要打成war包部署到容器中,Spring Boot只要打成一个可执行的 jar包就能独立运行,所有的依赖包都在一个jar包内。
  • 简化配置
    • spring-boot-starter-web启动器,内部已申明依赖其他组件,简少了maven的配置。
  • 自动配置
    • Spring Boot能根据当前类路径下的类、jar包来自动配置bean,如添加一个spring-boot-starter-web启动器就能拥有web的功能,无需其他配置。
  • 无代码生成和XML配置
    • Spring Boot配置过程中无代码生成,也无需XML配置文件就能完成所有配置工作,这一切都是借助于条件注解完成的,这也是Spring4.x的 核心功能之一。
  • 应用监控
    • Spring Boot提供一系列端点可以监控服务及应用,做健康检测。

Spring Boot 有哪些优点?

Spring Boot 的优点有

  • 减少开发,测试时间和努力。
  • 使用 JavaConfig 有助于避免使用 XML。
  • 避免大量的 Maven 导入和各种版本冲突。
  • 提供意见发展方法。
  • 通过提供默认值快速开始开发。
  • 没有单独的 Web 服务器需要。这意味着你不再需要启动 Tomcat,Glassfish或其他任何东西。
  • 需要更少的配置 因为没有 web.xml 文件。只需添加用@ Configuration 注释的类,然后添加用@Bean 注释的方法,Spring 将自动加载 对象并像以前一样对其进行管理。您甚至可以将@Autowired 添加到 bean 方法中,以使 Spring 自动装入需要的依赖关系中。
  • 基于环境的配置 使用这些属性,您可以将您正在使用的环境传递到应用程序:-Dspring.profiles.active = {enviornment}。在加载主应 用程序属性文件后,Spring 将在(application{environment} .properties)中加载后续的应用程序属性文件。

Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

  • 启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,它是一个复合注解,包含以下3个注解的功能
    • @SpringBootConfiguration,复合@Configuration注解,所以拥有作为容器类的功能。
    • @EnableAutoConfiguration,打开、关闭自动配置的功能,也可以关闭某个自动配置,例如关闭数据源自动配置,@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
    • @ComponentScan,配置包扫描路径,默认从启动类的包开始扫描

Spring Boot 的配置加载顺序是什么?

  • properties文件
  • YML或YAML文件
  • 系统环境变量
  • 命令行参数等

Spring Boot 的核心配置文件有哪几个?它们的区别是什么?

Spring Boot 的核心配置文件是 application 和 bootstrap 配置文件。

  • application 配置文件用于SpringBoot项目的配置文件。
  • bootstrap 配置文件则用于使用 Spring Cloud Config 配置中心时,从该文件中获取连接远程配置中心的配置信息。

MyBatis 面试题

什么是 Mybatis?

  • Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精力去处理加载驱 动、创建连接、创建statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。
  • MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数 以及获取结果集。3、通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java 对象和 statement 中 sql 的动态参数 进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。(从执行 sql 到返回 result 的过 程)。

Mybaits 的优点

  • 基于 SQL 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL 写在 XML 里,解除 sql 与程序代码的耦 合,便于统一管理;提供 XML标签,支持编写动态 SQL 语句,并可重用。
  • 与 JDBC 相比,减少了 50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接。
  • 很好的与各种数据库兼容(因为 MyBatis 使用 JDBC 来连接数据库,所以只要JDBC 支持的数据库 MyBatis 都支持)。
  • 能够与 Spring 很好的集成。
  • 提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射标签,支持对象关系组件维护。

MyBatis 框架的缺点

  • SQL 语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
  • SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

MyBatis 框架适用场合

  • MyBatis 专注于 SQL 本身,是一个足够灵活的 DAO 层解决方案。
  • 对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis 将是不错的选择。

MyBatis 与 Hibernate 有哪些不同?

  • Mybatis 和 hibernate 不同,它不完全是一个 ORM 框架,因为 MyBatis 需要程序员自己编写 Sql 语句。
  • Mybatis 直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需 求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是 mybatis 无法做到数据库无关性,如果需要实现支持多种数据库的软件, 则需要自定义多套 sql 映射文件,工作量大。
  • Hibernate 对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用 hibernate 开发可以节省很多代码,提高效率。

#{}${}的区别是什么?

  • #{}是预编译处理,${}是字符串替换。
  • Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的set 方法来赋值。
  • Mybatis 在处理${}时,就是把${}替换成变量的值。
  • 使用#{}可以有效的防止 SQL 注入,提高系统安全性。

当实体类中的属性名和表中的字段名不一样 ,怎么办?

  • 第一种,SQL语句中使用别名,让输出的字段名和实体的属性名一致。
  • 第二种,使用resultMap,定义实体属性和表字段名的映射关系。

MyBatis怎么确定Mapper方法唯一?允许参数不同的方法重载吗?

  • 通过Mapper类的全限定类名 + 方法名确定唯一,所以也不支持方法重载,如果需要不同参数的方法,使用不同的方法名即可。

如何获取自动生成的主键值

对于主键是自增形式的,可以使用如下方式:

  • 通过insert标签的属性,useGeneratedKeys属性开启获取主键功能,keyProperty指定模型中主键保存到的成员属性,keyColumn指定自增主键的列名,默认是表的第一列,当主键不是第一列时必须设置。
<insert id=”insertName”  useGeneratedKeys=”true” keyProperty=”id” keyColumn="id">
insert into names (name) values (#{name})
</insert>
复制代码

在 Mapper 中如何传递多个参数?

  • 方式一,Mapper方法形参上不做任何注解,在Xml中使用#{0},#{1},代表参数顺序获取(不推荐)。
  • 方式二,Mapper方法形参上增加@param注解,并可以指定映射的名称,在Xml中使用#{名称}的方式获取。
  • 方式三,使用一个Map存放键值对,Mapper方法形参上传入Map,但不需要@param注解,在Xml中使用#{keyName}来获取。

Mybatis 动态 sql 有什么用?有哪些动态 sql?

MyBatis提供动态SQL能力,执行时根据标签上的逻辑判断表达式,动态拼接SQL。 MyBatis提供了9个动态SQL标签。

  • trim
  • where
  • set
  • foreach
  • if
  • choose
  • when
  • otherwise
  • bind

Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,可以存在同名的方法吗?

  • 只要Xml文件指定的namespace不同即可,namespace一般是Mapper类的全限定类名,id则为方法名,确定唯一的方法名是通过namespace + id,实现的,所以只要namespace不同,就可以。

Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?

  • 在MyBatis的配置文件中配置,lazyLoadingEnabled=true|false,即可开启和关闭延迟加载。
  • 它的实现原理是,通过CGLIB给实体进行动态代理,当调用实体的getter方法时,动态代理拦截器的invoke()被调用,判断属性值值是否为null,为null,则调用相应的SQL进行查询,再set值到模型中进行返回。

Mybatis 的一级、二级缓存

  • 一级缓存默认开启,原理是一个基于PerpetualCache的HashMap,作用于是SqlSession,同一个SqlSession调用select查询时,如果命中缓存,则不进行查询,直接返回同一个模型对象,当进行增、删、改时,该缓存会被清除,SqlSession关闭时,也会清除。
  • 二级缓存默认不开启,二级缓存机制和一级缓存类似,也是通过一个基于PerpetualCache的HashMap,但是它的作用域是Mapper级别的,所以跨SqlSession进行缓存。同样当进行增、删、改时,缓存会被清空。

MyBatis 有几种分页方式?

4种方式

  • 一次性查询到内存,Java代码手动裁剪集合进行分页。
  • 手动sql中添加limit进行分页。
  • 使用分页拦截器,动态给sql添加limit。
  • 使用RowBounds,指定起始位置和查询条数,默认RowBounds其实位置0,查询条数为Integer.MAX_VALUE无限大。

Spring Cloud面试题

什么是 Spring Cloud?

  • Spring cloud 流应用程序启动器是基于 Spring Boot 的 Spring 集成应用程序,提供与外部系统的集成。Spring cloud Task,一个生命周期 短暂的微服务框架,用于快速构建执行有限数据处理的应用程序。

服务注册和发现是什么意思?Spring Cloud 如何实现?

  • 微服务越来越多,服务上、下线,或者服务的位置发生改变,手动更改很容易产生问题,Spring Cloud Eureka 服务则是自动化解决该问题,所有服务提供方都注册给Eureka,服务消费方则通过Eureka获取提供方的信息,并且Eureka和服务提供方有心跳检测,如果有服务挂了,会同步给服务消费方。

什么是微服务?

  • 微服务架构是一种架构模式或者说是一种架构风格,它提倡将单一应用程序划分为一组小的服务,每个服务运行在其独立的自己的进程中, 服务之间相互协调、互相配合,为用户提供最终价值。服务之间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API),每个服 务都围绕着具体的业务进行构建,并且能够被独立的构建在生产环境、类生产环境等。另外,应避免统一的、集中式的服务管理机制,对具 体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建,可以有一个非常轻量级的集中式管理来协调这些服务,可以 使用不同的语言来编写服务,也可以使用不同的数据存储。

什么是服务熔断?什么是服务降级?

  • 服务熔断,熔断机制是应对雪崩效应的一种微服务链路保护机制。当某个微服务不可用或者响应时间太长时,会进行服务降级,进而熔断该节点微服务 的调用,快速返回“错误”的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实 现,Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内调用20次,如果失败,就会启动熔断机制。
  • 服务降级,一般是从整体负荷考虑。就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调, 返回一个缺省值。这样做,虽然水平下降,但好歹可用,比直接挂掉强。

Eureka和zookeeper都可以提供服务注册与发现的功能,请说说两个的区别?

  • Zookeeper保证了CP(C:一致性,P:分区容错性),Eureka保证了AP(A:高可用)
    • 当向注册中心查询服务列表时,我们可以容忍注 册中心返回的是几分钟以前的信息,但不能容忍直接down掉不可用。也就是说,服务注册功能对高可用性要求比较高,但zk会出现这样一 种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新选leader。问题在于,选取leader时间过长,30 ~ 120s,且 选取期间zk集群都不可用,这样就会导致选取期间注册服务瘫痪。在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率 会发生的事,虽然服务能够恢复,但是漫长的选取时间导致的注册长期不可用是不能容忍的。
    • Eureka保证了可用性,Eureka各个节点是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点仍然可以提供注册和查询服务。 而Eureka的客户端向某个Eureka注册或发现时发生连接失败,则会自动切换到其他节点,只要有一台Eureka还在,就能保证注册服务可 用,只是查到的信息可能不是最新的。
    • 除此之外,Eureka还有自我保护机制,如果在15分钟内超过85%的节点没有正常的心跳,那么 Eureka就认为客户端与注册中心发生了网络故障,此时会出现以下几种情况
      • Eureka不在从注册列表中移除因为长时间没有收到心跳而应该过期的服务。
      • Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上(即保证当前节点仍然可用)
      • 当网络稳定时,当前实例新的注册信息会被同步到其他节点。因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况, 而不会像Zookeeper那样使整个微服务瘫痪。

微服务的优点缺点?说下开发项目中遇到的坑?

  • 优点

    • 每个服务直接足够内聚,代码容易理解
    • 开发效率高,一个服务只做一件事,适合小团队开发
    • 松耦合,有功能意义的服务
    • 可以用不同语言开发,面向接口编程
    • 易于第三方集成
    • 微服务只是业务逻辑的代码,不会和HTML,CSS或其他界面结合
    • 可以灵活搭配,连接公共库/连接独立库
  • 缺点

    • 分布式系统的责任性
    • 多服务运维难度加大
    • 系统部署依赖,服务间通信成本,数据一致性,系统集成测试,性能监控

Eureka和Zookeeper区别

  • Eureka取CAP的AP,注重可用性,Zookeeper取CAP的CP注重一致性。
  • Zookeeper在选举期间注册服务瘫痪,虽然服务最终会恢复,但选举期间不可用。
  • eureka的自我保护机制,会导致一个结果就是不会再从注册列表移除因长时间没收到心跳而过期的服务。依然能接受新服务的注册和查询 请求,但不会被同步到其他节点。不会服务瘫痪。
  • Zookeeper有Leader和Follower角色,Eureka各个节点平等。
  • Zookeeper采用过半数存活原则,Eureka采用自我保护机制解决分区问题。
  • eureka本质是一个工程,Zookeeper只是一个进程。

eureka自我保护机制是什么?

  • 当Eureka Server 节点在短时间内丢失了过多实例的连接时(比如网络故障或频繁启动关闭客户端)节点会进入自我保护模式,保护注册信息,不再删除注册数据,故障恢复时,自动退出自我保护模式。

什么是Ribbon?

  • ribbon是一个负载均衡客户端,可以很好的控制htt和tcp的一些行为。feign默认集成了ribbon。

什么是feigin?它的优点是什么?

  • feign采用的是基于接口的注解
  • feign整合了ribbon,具有负载均衡的能力
  • 整合了Hystrix,具有熔断的能力

Ribbon和Feign的区别?

  • Ribbon都是调用其他服务的,但方式不同。
  • 启动类注解不同,Ribbon是@RibbonClient feign的是@EnableFeignClients
  • 服务指定的位置不同,Ribbon是在@RibbonClient注解上声明,Feign则是在定义抽象方法的接口中使用@FeignClient声明。
  • 调用方式不同,Ribbon需要自己构建http请求,模拟http请求然后使用RestTemplate发送给其他服务,步骤相当繁琐。Feign需要将调用的方法定义成抽象方法即可。

springcloud断路器作用?

  • 当一个服务调用另一个服务由于网络原因或自身原因出现问题,调用者就会等待被调用者的响应 当更多的服务请求到这些资源导致更多的 请求等待,发生连锁效应(雪崩效应)
  • 断路器状态
    • 完全打开状态:一段时间内 达到一定的次数无法调用 并且多次监测没有恢复的迹象 断路器完全打开 那么下次请求就不会请求到该服务。
    • 半开:短时间内 有恢复迹象 断路器会将部分请求发给该服务,正常调用时断路器关闭。
    • 关闭:当服务一直处于正常状态 能正常调用。

##最后
今天的面试题分享就到这里了,说是200道,其实阿博也没怎么数过,有耐心的朋友可以数一下,评论区给阿博看看!

不过,惊喜还是要有的,关于面试题已经给大家整理完毕了~

如下图:


更有惊喜礼包~


如上,资料图已经清晰的给大家整理出来了,差不多就是类似这些内容,有需要获取的小伙伴,给阿博在评论区把666刷出来!!!

开个玩笑,有需要获取如上资料的粉丝朋友,评论转发后后台私信面试获取即可,是不是很方便,对,就是这么的方便,私信后,我会在后台一一回复大家!

那么,今天的分享就到这里,写完累的不要不要的,期待给大家的下次分享!!!

更多推荐

吐血整理!这200道阿里P6必备Java面试题,我简直太爱了