本文主要是介绍Java中高级面试题总览(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
基础
Java基础
JVM
多线程
jdk1.7源码
开源框架与技术
Spring
Netty
Dubbo
Zookeeper
Mybatis
elasticsearch
Kafka
SpringBoot
SpringCloud
架构
系统架构
分布式、高并发
系统设计、设计模式
性能优化
微服务
消息队列
数据库
关系型数据库
非关系型数据库
算法
项目
项目开发
业务问题
基础
Java基础
1、String为什么是final
2、java中的几种基本数据类型是什么_各自占用多少字节
3、string_stringbuffer_stringbuilder的区别
4、Vector,ArrayList, LinkedList 的区别
5、讲讲类的实例化顺序
父类静态变量、
父类静态代码块、
子类静态变量、
子类静态代码块、
父类匿名块
父类非静态变量(父类实例成员变量)、
父类构造函数、
子类匿名块
子类非静态变量(子类实例成员变量)、
子类构造函数。
6.HashSet 和 HashMap 的比较
7.HashTable, TreeMap,HashMap,ConcurrentHashMap 区别
8.HashMap有什么线程安全问题
9.HashMap是怎么扩容的(扩容多少,已有元素怎么处理的)
//默认的初始容量16,且实际容量是2的整数幂 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;//最大容量(传入容量过大将被这个值替换)
static final int MAXIMUM_CAPACITY = 1 << 30;// 默认加载因子为0.75(当表达到3/4满时,才会再散列),这个因子在时间和空间代价之间达到了平衡.更高的因子可以降低表所需的空间,但是会增加查找代价,而查找是最频繁操作
static final float DEFAULT_LOAD_FACTOR = 0.75f;// 最小树形化容量阈值:即 当哈希表中的容量 > 该值时,才允许树形化链表 (即 将链表 转换成红黑树)
// 为了避免扩容/树形化选择的冲突,这个值不能小于 4 * TREEIFY_THRESHOLD
static final int MIN_TREEIFY_CAPACITY = 64;
//桶的树化阈值:即 链表转成红黑树的阈值,在存储数据时,当链表长度 >= 8时,则将链表转换成红黑树
static final int TREEIFY_THRESHOLD = 8;// 桶的链表还原阈值:即 红黑树转为链表的阈值,当在扩容(resize())时(HashMap的数据存储位置会重新计算),在重新计算存储位置后,当原有的红黑树内数量 <= 6时,则将 红黑树转换成链表
static final int UNTREEIFY_THRESHOLD = 6;
选择6和8,中间有个差值7可以有效防止链表和树频繁转换
假设一下,如果设计成链表个数超过8则链表转换成树结构,链表个数小于8则树结构转换成链表,如果一个HashMap不停的插入、删除元素,链表个数在8左右徘徊,就会频繁的发生树转链表、链表转树,效率会很低。
初始容量值用户没指定的话默认使用DEFAULT_INITIAL_CAPACITY ,jdk1.8会自动根据指定的值把这个初始容量值改成最大于或等于指定值得2的n次幂的最小值,当实际容量大于扩容阀值时,把默认容量值扩大一倍并重新计算扩容阀值(DEFAULT_INITIAL_CAPACITY *DEFAULT_LOAD_FACTOR )
转红黑树的两个条件
一个是链表长度到8,一个是数组长度到64
Java 8中的散列值优化函数
为什么要异或呢?为什么要移位呢?而且移位16?
我们分析一下:
首先,假设有一种情况,对象 A 的 hashCode 为 1000010001110001000001111000000,对象 B 的 hashCode 为 0111011100111000101000010100000
如果数组长度是16,也就是 15 与运算这两个数, 你会发现结果都是0。这样的散列结果太让人失望了。很明显不是一个好的散列算法。
但是如果我们将 hashCode 值右移 16 位,也就是取 int 类型的一半,刚好将该二进制数对半切开。并且使用位异或运算(如果两个数对应的位置相反,则结果为1,反之为0),这样的话,就能避免我们上面的情况的发生。
总的来说,使用位移 16 位和 异或 就是防止这种极端情况。
但是,该方法在一些极端情况下还是有问题,比如:10000000000000000000000000 和 1000000000100000000000000 这两个数,如果数组长度是16,那么即使右移16位,在异或,hash 值还是会重复。
但是为了性能,对这种极端情况,JDK 的作者选择了性能。毕竟这是少数情况,为了这种情况去增加 hash 时间,性价比不高。
为什么cap要保持为2的幂次方
e.hash % newCap =e.hash &(newCap -1)
采用二进制位操作&,相对于%,能够提高运算效率,这就是cap的值被要求为2幂次的原因
HashMap看这篇就够了~_技术交流_牛客网
10、JAVA8 的 ConcurrentHashMap 为什么放弃了分段锁
而在1.8的时候摒弃了segment臃肿的设计,这种设计在定位到具体的桶时,要先定位到具体的segment,然后再在segment中定位到具体的桶。而到了1.8的时候是针对的是Node[] tale数组中的每一个桶,进一步减小了锁粒度。并且防止拉链过长导致性能下降,当链表长度大于8的时候采用红黑树的设计。
主要设计上的变化有以下几点:
1.不采用segment而采用node数组,锁住node来实现减小锁粒度。
2.设计了MOVED状态 当resize的中过程中 线程2还在put数据,线程2会帮助resize。
3.使用3个CAS操作来确保node的一些操作的原子性,这种方式代替了锁。
4.sizeCtl的不同值来代表不同含义,起到了控制的作用。
5.采用synchronized而不是ReentrantLock延伸:
为什么是synchronized,而不是可重入锁
1. 减少内存开销
假设使用可重入锁来获得同步支持,那么每个节点都需要通过继承AQS来获得同步支持。但并不是每个节点都需要获得同步支持的,只有链表的头节点(红黑树的根节点)需要同步,这无疑带来了巨大内存浪费。
2. 获得JVM的支持
可重入锁毕竟是API这个级别的,后续的性能优化空间很小。 synchronized则是JVM直接支持的,JVM能够在运行时作出相应的优化措施:锁粗化、锁消除、锁自旋等等。这就使得synchronized能够随着JDK版本的升级而不改动代码的前提下获得性能上的提升。
11、有没有有顺序的Map实现类,如果有,他们是怎么保证有序的
LinkedHashMap: LinkedHashMap维护了一个双向链表来记录键值对的插入顺序或者访问顺序。默认情况下,LinkedHashMap根据插入顺序来维护键值对的顺序,但是也可以在构造函数中启用访问顺序模式,使得最近访问的元素被放到链表的末尾。当迭代LinkedHashMap时,键值对会按照这个顺序进行返回。
TreeMap: TreeMap实现了SortedMap接口,它基于红黑树(一种自平衡的二叉查找树)。TreeMap可以根据键的自然顺序(例如,如果键实现了Comparable接口)或者根据构造TreeMap时提供的Comparator来维护键的顺序。因此,当遍历TreeMap时,它会按照键的排序顺序返回键值对。
12.讲讲你理解的 nio,他和 bio 的区别是啥
13.Java NIO和IO的主要区别
NIO 和传统 IO 之间第一个最大的区别是, IO 是面向流的, NIO 是面向缓冲区的
14、nio的底层实现原理
1.由一个专门的线程来处理所有的IO事件,并负责分发。
2.事件驱动机制:事件到的时候触发,而不是同步的去监视事件。
3.线程通讯:线程之间通过wait,notify等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。
在NIO中有几个核心对象需要掌握:缓冲区(Buffer)、通道(Channel)、选择器(Selector)。
Buffer实际上是一个容器对象,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写
入数据时,它也是写入到缓冲区中的Channel是一个对象,通过它可以读取和写入数据,当然了所有数据都通过Buffer对象来处理。读取数据Channel->Buffer, 写入数据Buffer->Channel
Selector就是注册各种IO事件的地方,而且当那些事件发生时,就是这个对象告诉我们所发生的事件。
Reactor网络线程模型-CSDN博客
15、static 方法能不能被 overload,override?
在Java中,
static
方法可以被重载(overload),但不能被重写(override)
public class Example {public static void foo() {System.out.println("foo()");}public static void foo(int a) {System.out.println("foo(int)");}public static void foo(String a) {System.out.println("foo(String)");}
}
16.反射的原理
JAVA语言编译之后会生成一个.class文件,反射就是通过字节码文件找到某一个类、类中的方法以及属性等。
17.反射创建类实例的三种方式是什么
18.反射中,Class.forName 和ClassLoader.loadClass()区别
19.描述动态代理的几种实现方式,分别说出相应的优缺点
20.jdk动态代理与cglib实现的区别
JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,这就是为什么采用动态代理时为什么只
能用接口引用指向代理,而不能用传入的类引用执行动态类。CGLib采用的是用创建一个继承实现类的子类,用asm库动态修改子类的代码来实现的,所以可以用传入的类引
用执行代理类CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花
费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更
为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理
21.为什么CGlib方式可以对接口实现代理
CGLib代理的原理是通过二进制流生成一个动态class,该class继承被代理类以实现动态代理。那么就提供了一个代理无实现类的接口的可能
有接口的CGLIB动态代理实现_浪丶荡-CSDN博客_cglib代理接口
22、如何在父类中为子类自动完成所有的 hashcode和equals实现?这么做有何优劣
23、拦截器和过滤器的区别
拦截器(Interceptors):
- 拦截器通常与特定的Web框架(如Spring MVC)紧密集成,并且能够深度参与请求处理的整个过程。
- 拦截器可以访问框架的执行上下文、控制器和处理方法的元数据。
- 拦截器可以在请求处理的多个点进行操作,例如在控制器方法执行之前、之后以及请求完成后处理视图渲染之前。
- 拦截器可以链式调用,即可以按照特定的顺序执行多个拦截器。
- 拦截器通常用于跨越框架的任务,如权限检查、日志记录、事务处理等。
过滤器(Filters):
- 过滤器是基于Java Servlet规范的组件,因此它们与Servlet容器紧密相关,通常在Web框架之前执行。
- 过滤器对请求和响应进行预处理和后处理,但通常不了解请求的具体内容。
- 过滤器可以对几乎所有请求进行操作,它们在Servlet容器接收到请求和响应客户端之前执行。
- 过滤器也支持链式调用,允许多个过滤器按照特定顺序处理请求。
- 过滤器常用于处理通用任务,如请求日志记录、压缩响应数据、跨站点请求伪造(CSRF)防护等。
总的来说,拦截器与Web框架紧密集成,提供了对请求处理更细粒度的控制,而过滤器则更接近于底层的Servlet API,并且在请求进入Web框架之前对其进行处理。根据应用程序的具体需求和所使用的技术栈,开发人员可以选择拦截器、过滤器或两者结合使用以实现特定的功能。
24.数组和链表数据结构描述,各自的时间复杂度
25.error和exception的区别,CheckedException,RuntimeException的区别
1.Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。
2.Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
Exception又分为两类
CheckedException:(编译时异常) 需要用try——catch显示的捕获,对于可恢复的异常使用CheckedException。
UnCheckedException(RuntimeException):(运行时异常)不需要捕获,对于程序错误(不可恢
复)的异常使用RuntimeException。
26.请列出 5 个运行时异常
27.在自己的代码中,如果创建一个 java.lang.String 类,这个类是否可以被类加载器加载?
28.说一说你对 java.lang.Object 对象中 hashCode 和 equals 方法的理解。在什么场景下需要重新实现这两个方法
29在jdk1.5中,引入了泛型,泛型的存在是用来解决什么问题
30Switch能否用string做参数
jdk1.7后,整形,枚举类型,boolean,字符串都可以。
31.序列化和反序列化
32.写出三种单例模式实现
33.有没有可能 2 个不相等的对象有相同的 hashcode
有可能
34.Java的四种引用类型,强弱软虚,用到的场景
Java中的引用类型有4种,由强到弱依次如下:
1) 强引用(StrongReference)是使用最普遍的引用,类似:“Object obj = new Object()” 。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
2) 软引用(Soft Reference)是用来描述一些有用但并不是必需的对象,如果内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
3) 弱引用(WeakReference)也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前, 当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
4) 虚引用(PhantomReference)也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。需要和引用队列(ReferenceQueue)联合使用,主要用于跟踪一个引用对象被垃圾回收的过程,gc回收时会放入队列里也无法通过虚引用来取得一个对象实例。
35.Hashcode的作用
36.object常用的方法
37.Collection 与 Collections 的区别
java.util.Collection 是一个集合接口
Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
38、Java8新特性
Java 8更新:新特性与优化一览_java 8 count 会不会返回null-CSDN博客
39、final 的用途
在Java中,`final` 关键字可以用在三个地方:类,方法和变量。
- 如果一个类被声明为 `final`,意味着这个类不能被继承。这在你希望类的行为不被修改,或者出于安全考虑不希望类被继承时很有用。
- 如果一个方法被声明为 `final`,意味着这个方法不能被子类覆盖(重写)。如果你希望类的某个行为是固定的,不允许任何修改,就可以使用 `final` 方法。
- 如果一个变量被声明为 `final`,意味着这个变量的值一旦被初始化后,就不能再被改变。对于基本类型,这意味着变量的值不能改变;对于引用类型,这意味着引用不能改变,但引用的对象本身是可以被修改的。
`final` 的主要目的是定义不可改变的事物。这对于提高安全性和改善性能都有好处。
40、字符流和字节流的区别
字符流与字节流都是Java I/O系统中处理数据的方式,它们的主要区别如下:
数据单位:字节流是以字节(8位二进制数)为单位进行数据的读写操作,而字符流是以字符为单位进行数据的读写操作。字符通常是16位或32位的Unicode字符。
适用场景:字节流主要用于处理二进制数据,如图片、音频、视频等,以及其他任意类型的数据。而字符流主要用于处理文本数据,尤其是那些需要考虑字符编码的情况。
Java类层次结构:字节流的基类是
InputStream
和OutputStream
,分别负责输入和输出。字符流的基类是Reader
和Writer
,也分别负责输入和输出。缓冲处理:字符流通常具有缓冲处理的功能,如
BufferedReader
和BufferedWriter
,它们可以提高字符读写的性能。而字节流需要显式地创建缓冲流,如BufferedInputStream
和BufferedOutputStream
。字符编码:字节流在操作数据时不考虑字符编码,而字符流在读写数据时需要处理字符编码。字符流的转换器
InputStreamReader
和OutputStreamWriter
可以将字节流转换为字符流,同时处理字符编码问题。总结:字节流通常用于处理二进制数据,而字符流用于处理文本数据。字节流可以处理任意类型的数据,而字符流更适合处理需要考虑字符编码的情况。选择字节流还是字符流取决于具体的应用场景。
41、Servlet是否是线程安全?如果不安全怎么处理 ?
Servlet是J2EE的一种技术,主要用于处理客户端的请求并生成动态内容,Spring MVC,是建立在Servlet API之上。Servlet的工作原理可以简单地总结如下:
- 当客户端(通常是Web浏览器)发送HTTP请求到服务器时,Web服务器首先接收到这个请求。
- Web服务器根据请求信息,找到对应的Servlet进行处理。如果这是Servlet的第一次请求,Web服务器会创建一个新的Servlet实例。
- Web服务器调用Servlet的init()方法进行初始化,然后调用Servlet的service()方法来处理请求。service()方法会根据请求类型(GET、POST、PUT、DELETE等)调用对应的doGet()、doPost()等方法。
- Servlet生成HTTP响应并将其发送回Web服务器,Web服务器再将这个响应发送回客户端。
- 当Web服务器关闭或者Servlet被卸载时,Web服务器会调用Servlet的destroy()方法进行清理操作。
Servlet的主要优点是效率高、执行快、易于使用。Servlet在服务器上是以多线程的方式运行的,当多个用户同时访问一个Servlet时,服务器只需要创建一个新的线程来处理请求,而不需要创建一个新的Servlet实例,所以Servlet具有很高的处理效率。
42、stringbuffer内部的数据结构在字符串变化时怎么操作
String 类不可变,内部维护的char[] 数组长度不可变,为final修饰,String类也是final修饰,不存在扩
容。字符串拼接,截取,都会生成一个新的对象。频繁操作字符串效率低下,因为每次都会生成新的对象。 StringBuilder 类内部维护可变长度char[] , 初始化数组容量为16,存在扩容, 其append拼接字符串方法内
部调用System的native方法,进行数组的拷贝,不会重新生成新的StringBuilder对象。非线程安全的字符串操
作类, 其每次调用 toString方法而重新生成的String对象,不会共享StringBuilder对象内部的char[],会进
行一次char[]的copy操作。 StringBuffer 类内部维护可变长度char[], 基本上与StringBuilder一致,但其为线程安全的字符串操作类,
大部分方法都采用了Synchronized关键字修改,以此来实现在多线程下的操作字符串的安全性。其toString方法
而重新生成的String对象,会共享StringBuffer对象中的toStringCache属性(char[]),但是每次的
StringBuffer对象修改,都会置null该属性值
43、new一个Object对象占用多少内存
如果JDK64位,8字节是引用,16字节是堆内存(对象头大小),总共是8+16=24字节,所以new一个Object对象占
用8+16=24字节。如果JDK是32位,按如上分析方法可知new一个Object对象占用4+8=12字节
【转】Java new一个Object对象占用多少内存? - MERRU - 博客园
JVM
1.GC 如何判断对象失去引用
引用计数算法(已废弃)
可达性分析
引用计数算法简单高效,早期的Java虚拟机中使用这个方式,但是正如上面提到的不能解决“引用闭环”的问题,后来的Java虚拟机中普遍采用根集算法。从GCRoot(比如一个静态变量)开始遍历引用关系,能遍历到的,叫做引用可达,遍历不到的叫做不可达。不可达的对象就被判“死刑了”,GC的时候将被枪毙掉。
可以作为GCRoots的对象包括下面几种:
(1). 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。
(2). 方法区中的类静态属性引用的对象。
(3). 方法区中常量引用的对象。
(4). 本地方法栈中JNI(Native方法)引用的对象。
2.JVM崩溃原因查找?(jvm内存镜像文件分析,常用jvm性能分析命令)
3.内存泄露、溢出的异同
1、内存泄漏是指分配出去的内存无法回收了
2、内存溢出是指程序要求的内存,超出了系统所能分配的范围,从而发生溢出。
4.如何检测内存泄露?
OOM是内存泄漏的常见指示
Java heap space 不一定意味着内存泄漏
PermGen space 此错误消息表明永久代已满。
Requested array size exceeds VM limit 此错误表示应用程序(或该应用程序使用的API)尝试分配大于堆大小的数组
Request bytes for . Out of swap space 当本机堆的分配失败并且本机堆可能将被耗尽时,HotSpot VM会抛出此异常
Native method 在JNI或本机方法中检测到Java内存分配失败,而不是在Java VM代码中检测到
Application Crash Without OOM 应用程序可能会在从本机堆分配失败后很快崩溃
Java VisualVM执行内存泄漏检测的工具
5.怎么打出线程栈信息
jstack
6.如何使对象 GC 后再活一次
- 在Java中,对象在被垃圾回收(GC)前,可以通过重写 `finalize()` 方法使对象"再活一次"。 `finalize()` 方法是Object类的一个方法,它会在垃圾收集器删除对象之前被调用。
- 在 `finalize()` 方法中,如果重新将对象赋值给一个静态变量或者和某个根节点建立关联,那么这个对象就有可能在垃圾收集后再次被使用。
这是一个简单的例子:
public class TestGC {public static TestGC SAVE_HOOK = null;public void isAlive() {System.out.println("I am still alive :)");}@Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println("finalize method executed!");TestGC.SAVE_HOOK = this;}public static void main(String[] args) throws Exception {SAVE_HOOK = new TestGC();// 对象第一次成功拯救自己SAVE_HOOK = null;System.gc();// 因为finalize方法优先级很低,所以暂停0.5秒以等待它Thread.sleep(500);if (null != SAVE_HOOK) {SAVE_HOOK.isAlive();} else {System.out.println("no, i am dead :(");}// 下面这段代码与上面的完全相同,但是这次自救却失败了SAVE_HOOK = null;System.gc();Thread.sleep(500);if (null != SAVE_HOOK) {SAVE_HOOK.isAlive();} else {System.out.println("no, i am dead :(");}}
}
然而,需要注意的是,这种在 `finalize()` 方法中"自救"的方式并不推荐。因为对于同一个对象, `finalize()` 方法只会被调用一次。也就是说,如果同一个对象再一次成为了垃圾,那么它的 `finalize()` 方法将不会被再次调用,这就是上面代码中第二次自救失败的原因。此外, `finalize()` 方法的执行速度较慢,且不确定性大,可能会导致各种问题。所以,实际开发中我们一般不会重写 `finalize()` 方法,而是通过其他方式管理资源。
7. jvm内存哪些是线程共享,哪些是线程独享
- java8之前
线程共享:堆,非堆,本地方法栈
线程独享:栈,程序计数器
- java8及以后
线程共享:堆,本地方法栈
线程独享:栈,程序计数器
8.什么情况下会发生栈内存溢出?
9.出现了内存溢出OOM,你怎么排错
Java.lang.OutOfMemeoryError:GC overhead limit exceeded
如上异常,即程序在垃圾回收上花费了98%的时间,却收集不回2%的空间,通常这样的异常伴随着CPU的冲高。定位方法同上(内存泄漏)
Java.lang.OutOfMemoryError: PermGen space(JAVA8引入了Metaspace区域)
永久代内存被耗尽(内存溢出)
Java.lang.OutOfMemeoryError:Unable to create new native thread
Java应用已经达到它可以运行的线程数的上限.
解决方法
1、检查是否存在大对象的分配,最有可能的是大数组分配
2、通过jmap命令,把堆内存dump下来,使用mat工具分析一下,检查是否存在内存泄露的问题
3、如果没有找到明显的内存泄露,使用 -Xmx 加大堆内存
4、还有一点容易被忽略,检查是否有大量的自定义的 Finalizable 对象,也有可能是框架内部提供的,考虑其存在的必要性几种OOM异常分析_sunquan291的专栏-CSDN博客_oom异常
10.JVM的工作原理(内存模型)
11.Eden和Survivor比例
12.常见JVM参数列表
13.JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和Survivor?
14.jvm中一次完整的 GC流程是怎样的?
15.你知道哪几种垃圾收集器,各自的优缺点,重点讲下 cms,包括原理,流程,优缺点
16.JVM为什么需要GC
多线程
1.thread类常用方法
2.Java 实现多线程的方式有哪些
3.什么是线程安全
4、volatile的原理,作用,能代替锁么
volatile关键字内存栅栏有两层意思:
1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
2)在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
5.画一个线程的生命周期状态图
在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)五种状态
6.sleep和wait的区别
7.Lock与Synchronized的区别
8.ReentrantLock 获取锁定的四种方式
9.解释以下名词:重排序,自旋锁,偏向锁,轻量级锁,可重入锁,公平锁,非公平锁,乐观锁,悲观锁
锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁
重量级锁( Mutex Lock)
Synchronized 是通过对象内部的一个叫做监视器锁( monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的 Mutex Lock 来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized 效率低的原因。因此, 这种依赖于操作系统 Mutex Lock 所实现的锁我们称之为“重量级锁” 。 JDK 中对 Synchronized 做的种种优化,其核心都是为了减少这种重量级锁的使用。JDK1.6 以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了“轻量级锁”和“偏向锁”。轻量级锁
锁升级随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁(但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级)。“ 轻量级” 是相对于使用操作系统互斥量来实现的传统锁而言的。但是,首先需要强调一点的是,轻量级锁并不是用来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用产生的性能消耗。在解释轻量级锁的执行过程之前, 先明白一点,轻量级锁所适应的场景是线程交替执行同步块的情况,如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁。
偏向锁
Hotspot 的作者经过以往的研究发现大多数情况下锁不仅不存在多线程竞争,而且总是由同一线程多次获得。 偏向锁的目的是在某个线程获得锁之后,消除这个线程锁重入(CAS)的开销,看起来让这个线程得到了偏护。 引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径。
自旋锁
自旋锁原理非常简单, 如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁
的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。线程自旋是需要消耗 cup 的,说白了就是让 cup 在做无用功,所以需要设定一个自旋等待的最大时间。如果持有锁的线程执行的时间超过自旋等待的最大时间扔没有释放锁,就会导致其它争用锁的线程在最大等待时间内还是获取不到锁,这时争用线程会停止自旋进入阻塞状态。自旋锁的优缺点:自旋锁尽可能的减少线程的阻塞,这对于锁的竞争不激烈,且占用锁时间非常短的代码块来说性能能大幅度的提升,因为自旋的消耗会小于线程阻塞挂起再唤醒的操作的消耗,这些操作会导致线程发生两次上下文切换!
但是如果锁的竞争激烈,或者持有锁的线程需要长时间占用锁执行同步块,这时候就不适合使用自旋锁了,因为自旋锁在获取锁前一直都是占用 cpu 做无用功。公平锁( Fair)
加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得非公平锁( Nonfair)
加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待
1. 非公平锁性能比公平锁高 5~10 倍,因为公平锁需要在多核的情况下维护一个队列
2. Java 中的 synchronized 是非公平锁, ReentrantLock 默认的 lock()方法采用的是非公锁。
可重入锁(递归锁)
本文里面讲的是广义上的可重入锁,而不是单指 JAVA 下的 ReentrantLock。 可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。在 JAVA 环境下 ReentrantLock 和 synchronized 都是 可重入锁
乐观锁
乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作。java 中的乐观锁基本都是通过 CAS 操作实现的, CAS 是一种更新的原子操作, 比较当前值跟传入值是否一样,一样则更新,否则失败。
悲观锁
悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会 block 直到拿到锁。java中的悲观锁就是 Synchronized。AQS框架下的锁则是先尝试 cas乐观锁去获取锁,获取不到,才会转换为悲观锁,如 RetreenLock。
10.重排序
11.用过哪些原子类,他们的原理是什么
12.线程池原理,并说说newCache和newFixed有什么区别,构造函数的各个参数的含义是什么
13.线程池的关闭方式有几种,各自的区别是什么
shutdown()
调用后,不可以再 submit 新的 task,已经 submit 的将继续执行shutdownNow()
调用后,试图停止当前正在执行的 task,并返回尚未执行的 task 的 list
14.假如有一个第三方接口,有很多个线程去调用获取数据,现在规定每秒钟最多有10个线程同时调用它,如何做到
Semaphore类是一个计数信号量,必须由获取它的线程释放, 通常用于限制可以访问某些资源(物理或逻辑的)线程数目。
15.spring的controller是单例还是多例,怎么保证并发的安全
- Spring的Controller默认是单例的,这意味着Spring容器启动时,会为每个Controller创建一个实例,所有的HTTP请求都由这个单一实例来处理。
- 如果在Controller中定义了类变量(也称为状态变量),那么这些变量是被所有线程共享的,这时候就可能出现线程安全问题。为了避免这类问题,我们应该尽量避免在Controller中定义类变量,或者使用线程安全的数据结构来存储类变量。
16、用三个线程按顺序循环打印 abc 三个字母,比如 abcabcabc
public class PrintABC {private static int state = 0;public static void main(String[] args) {final Object object = new Object();new Thread(() -> {for (int i = 0; i < 10; ) {synchronized (object) {while (state % 3 != 0) {try {object.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print("a");state++;object.notifyAll();i++;}}}).start();new Thread(() -> {for (int i = 0; i < 10; ) {synchronized (object) {while (state % 3 != 1) {try {object.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print("b");state++;object.notifyAll();i++;}}}).start();new Thread(() -> {for (int i = 0; i < 10; ) {synchronized (object) {while (state % 3 != 2) {try {object.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print("c");state++;object.notifyAll();i++;}}}).start();}
}
17.ThreadLocal原理是什么,用的时候要注意什么
18.AQS同步器的实现原理(AbstractQueuedSynchronizer)
19.countdowlatch内部原理和用法(比如countdownlatch的await方法是怎么实现的)
CountDownLatch是基于AQS的共享锁来实现的,由于内部类继承了AQS,所以它内部也是FIFO队列,同时也一样是
前驱节点唤醒后继节点,不能像CyclicBarrier那样使用完毕后还可以复用;
1任务分为N个任子线程去执行,state也初始化为N(注意N要要与线程个数一致)
2这N个线程也是并行执行的,每个子线程执行完后后countDown()一次,state会CAS减1
3等到所有子线程都执行完成之后即(state==0),会调用LockSupport.unpark(s.thread)
4然后主调用会从await()函数返回(之前是通过LockSupport.park(this);阻塞),继续后余动作
20.cyclicbarrier 的内部原理和用法
CyclicBarrier实现主要基于ReentrantLock默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告
诉CyclicBarrier已经到达屏障位置,线程被阻塞。另外一个构造方法CyclicBarrier(int parties, Runnable barrierAction),其中barrierAction任务会在
所有线程到达屏障后执行。
深入浅出java CyclicBarrier - 简书
21.Lock锁的实现原理
需要实现锁的功能,两个必备元素:一个是表示(锁)状态的变量(我们假设0表示没有线程获取锁,1表示已有线程占有锁),该变量必须声明为
voaltile类型;
另一个是队列,队列中的节点表示因未能获取锁而阻塞的线程。
22、Synchronized对象锁和类锁的区别
23、Synchronized的原理是什么
对象头加锁底层用 Mutex Lock
24.进程和线程的区别
25.如果让你实现一个并发安全的链表,你会怎么做
26.多线程如果线程挂住了怎么办
27.简述ConcurrentLinkedQueue和LinkedBlockingQueue 的用处和不同之处
28.Queue添加数据方法add()put()offer()不同之处
add方法在添加元素的时候,若超出了度列的长度会直接抛出异常
offer方法在添加元素时,如果发现队列已满无法添加的话,会直接返回false。
put方法,若向队尾添加元素的时候发现队列已经满了会发生阻塞一直等待空间,以加入元素。
29.导致线程死锁的原因?怎么解除线程死锁
30、编写一段死锁代码
//首先我们先定义两个final的对象锁.可以看做是共有的资源.final Object lockA = new Object();final Object lockB = new Object();
//生产者Aclass ProductThreadA implements Runnable{@Overridepublic void run() {
//这里一定要让线程睡一会儿来模拟处理数据 ,要不然的话死锁的现象不会那么的明显.这里就是同步语句块里面,首先获得对象锁lockA,然后执行一些代码,随后我们需要对象锁lockB去执行另外一些代码.synchronized (lockA){//这里一个log日志Log.e("CHAO","ThreadA lock lockA");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lockB){//这里一个log日志Log.e("CHAO","ThreadA lock lockB");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}}}//生产者Bclass ProductThreadB implements Runnable{//我们生产的顺序真好好生产者A相反,我们首先需要对象锁lockB,然后需要对象锁lockA.@Overridepublic void run() {synchronized (lockB){//这里一个log日志Log.e("CHAO","ThreadB lock lockB");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lockA){//这里一个log日志Log.e("CHAO","ThreadB lock lockA");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}}}//这里运行线程
ProductThreadA productThreadA = new ProductThreadA();
ProductThreadB productThreadB = new ProductThreadB();Thread threadA = new Thread(productThreadA);
Thread threadB = new Thread(productThreadB);
threadA.start();
threadB.start();
当threadA开始执行run方法的时候,它会先持有对象锁localA,然后睡眠2秒,这时候threadB也开始执行run方法,它持有的是localB对象锁.当threadA运行到第二个同步方法的时候,发现localB的对象锁不能使用(threadB未释放localB锁),threadA就停在这里等待localB锁.随后threadB也执行到第二个同步方法,去访问localA对象锁的时候发现localA还没有被释放(threadA未释放localA锁),threadB也停在这里等待localA锁释放.就这样两个线程都没办法继续执行下去,进入死锁的状态.
31用过读写锁吗,原理是什么,一般在什么场景下用
ReentrantReadWriteLock是一个解决单线程写和多线程读的理想方法。它采用类似于读写分离的思路设定了读锁和写锁。对于这两个锁的访问保证尽可能大的读并行和写互斥。另外,在一定的条件下写锁可以转换成读锁,而读锁却不能转换成写锁。
ReentrantReadWriteLock却可以有锁的机制保证互斥。它同时也尽可能保证了足够大的并行性。
32.两个进程能否共享内存空间
33锁的等级:方法锁、对象锁、类锁
34.多线程模拟实现生产者/消费者模型
35.开启多个线程,如何保证顺序执行,有哪几种实现方式,或者如何保证多个线程都执行完再拿到结果
36、延迟队列的实现方式,delayQueue 和时间轮算法的异同
37、非常多个线程(可能是不同机器)相互之间需要等待协调,才能完成某种工作,问怎么设计这种协调方案
38、怎么实现分布式锁(redis+zookeeper)
39、什么是锁升级降级
40、线程池队列满的时候怎么处理
41.java线程能被中断么,中断一定能成功么
42、线程启动用 start 方法还是 run
43、线程常用的并发类及关键字
44、有哪些无锁数据结构,无锁实现的原理是什么
45、i++是不是线程安全,为什么
1.不安全的,因为在操作i++的时候,是分步骤做的,可以理解为:
- tp = i;
- tp2 = i+1;
- i=tp2;
如果线程1在执行第一条代码的时候,线程2访问i变量,这个时候,i的值还没有变化,还是原来的值,所以是不安全的
2.这也是经典的内存不可见问题,那么把 i加上 volatile 让内存可见是否能解决这个问题呢? 答案是:不能。因为 volatile 只能保证可见性,不能保证原子性。多个线程同时读取这个共享变量的值,就算保证其他线程修改的可见性,也不能保证线程之间读取到同样的值然后相互覆盖对方的值的情况。
46、说说 JDK1.6 之后的synchronized 关键字底层做了哪些优化,可以详细介绍一下这些优化吗
JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。
锁主要存在四中状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。注意锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。
关于这几种优化的详细信息可以查看:synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比
jdk1.7源码
1.HashMap的实现原理
2.HashSet的实现原理
3.Hashtable的实现原理
HashMap结构中,是允许保存
null
的,Entry.key
和Entry.value
均可以为null
。但是HashTable中是不允许保存null
的
HashTable类继承自Dictionary
类,实现了三个接口,分别是Map
,Cloneable
和java.io.Serializable
HashTable的主要方法的源码实现逻辑,与HashMap中非常相似,有一点重大区别就是所有的操作都是通过synchronized
锁保护的。只有获得了对应的锁,才能进行后续的读写等操作。
4.LinkedHashMap的实现原理
5.LinkedHashSet 的实现原理
LinkedHashSet 底层使用 LinkedHashMap 来保存所有元素,它继承与 HashSet,其所有的方法操作上又与 HashSet 相同
6.ArrayList的实现原理
7.LinkedList的实现原理
LinkedList存储元素的数据结构是双向链表结构,由存储元素的结点连接而成,每一个节点都包含前一个节点的引用,后一个节点的引用和节点存储的值。当一个新节点插入时,只需要修改其中保持先后关系的节点的引用即可。
8.ConcurrentHashMap的实现原理
开源框架与技术
Spring
1.Spring加载流程(bean初始化)
2.Spring事务管理总结
3.spring的事务传播属性以及隔离级别
4.Springmvc中DispatcherServlet初始化过程
5.Bean如何优雅的销毁
context.registerShutdownHook(); 是一个钩子方法,当jvm关闭退出的时候会调用这个钩子方法,在设计模式之 模板模式中 通过在抽象类中定义这样的钩子方法由实现类进行实现,这里的实现类是AbstractApplicationContext,这是spring 容器优雅关闭的方法
Spring 的 init-method 和 destory-method_安德里亚的成长-CSDN博客_init-method=
6.简述一下Spring结构
7.什么是IOC
8.spring:AOP的实现原理
核心概念
1、切面( aspect) : 类是对物体特征的抽象,切面就是对横切关注点的抽象
2、横切关注点: 对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。
3、连接点( joinpoint) : 被拦截到的点,因为 Spring 只支持方法类型的连接点,所以在 Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。
4、切入点( pointcut) : 对连接点进行拦截的定义
5、通知( advice) : 所谓通知指的就是指拦截到连接点之后要执行的代码, 通知分为前置、后置、异常、最终、环绕通知五类。
6、目标对象: 代理的目标对象
7、织入( weave) : 将切面应用到目标对象并导致代理对象创建的过程
Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,具体使用哪种方式生成由
AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。 默认的策略是如果目标类是接口,
则使用 JDK 动态代理技术,否则使用 Cglib 来生成代理。
9.Spring的生命周期
10.BeanFactory和ApplicationContext区别
11.Spring实例化Bean的方式
12.Spring各种事务实现及区别
13.Spring编程事务与声明事务的区别
14.SpringMVC的工作原理
15.Spring怎么配置事务(具体说出一些关键的xml元素)
16.springmvc用到的注解,作用是什么,原理
17.springboot启动机制
18.Spring循环依赖的处理(单例、多例状态下的处理,懒加载一定能处理掉循环依赖么?)
19.@Autowired 与@Resource和@Qualifier注解的区别
@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了
@Autowired是根据类型进行自动装配的。如果当Spring上下文中存在不止一个UserDao类型的bean时,就会抛出BeanCreationException异常;如果Spring上下文中不存在UserDao类型的bean,也会抛出BeanCreationException异常。我们可以使用@Qualifier配合@Autowired来解决这些问题
可能存在多个UserDao实例
@Autowired
public void setUserDao(@Qualifier("userDao") UserDao userDao) {
this.userDao = userDao;
}
20.@transactional注解在什么情况下会失效,为什么
1、service类标签(一般不建议在接口上)上添加@Transactional,可以将整个类纳入spring事务管理,在每个业务方法执行时都会开启一个事务,不过这些事务采用相同的管理方式。
2、@Transactional 注解只能应用到 public 可见度的方法上。 如果应用在protected、private或者 package可见度的方法上,也不会报错,不过事务设置不会起作用。
3、默认情况下,Spring会对unchecked异常进行事务回滚;如果是checked异常则不回滚。 通俗一点:你写代码出现的空指针等异常,会被回滚,文件读写,网络出问题,spring就没法回滚了
Netty
1.netty的线程模型是什么?netty是如何基于reactor模型上实现的
2.netty的HashedWheelTimer 的用法,实现原理,是否出现过调用不够准时,怎么解决
3.netty的心跳处理在弱网下怎么办
4.netty的通讯协议是什么样的
https://blog.csdn.net/u013252773/article/category/2104567
5.netty原理
Dubbo
1.说下Dubbo的架构图和组件
2.Dubbo容错机制有哪些?分别适用那些场景?
3.Dubbo线程模型
Dubbo的 protocol标签提供了三个参数 dispatcher,threads(默认为100)和 threadpool来为我们自定义DUBBO协议下的线程模型
Dubbo的线程模型_击水三千里的专栏-CSDN博客_dubbo 线程模型
4.Dubbo负载均衡策略与算法
5.Dubbo结果缓存
dubbo提供了三种结果缓存机制:lru:基于最近最少使用原则删除多余缓存,保持最热的数据被缓存
threadlocal:当前线程缓存
jcache:可以桥接各种缓存实现使用方式
<dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"><dubbo:method name="sayHello" timeout="60000" cache="lru"/>
</dubbo:reference>
6.dubbo怎么服务降级
第1种
在远程调用异常时,服务端直接返回一个固定的字符串(也就是写死的字符串)
具体配置:
在服务调用方配置mock参数:
<dubbo:reference id="xxxService" interface="com.x..service.xxxxService" check="false"
mock="return 123456..." />
说明:配置了mock参数之后,假设在调用服务的时候,远程服务没有启动,或者各种网络异常了,那远程服务
会把这个mock配置的值返回,也就是会返回123456...第2种
在远程调用异常时,服务端根据自定义mock业务处理类进行返回)
具体配置:
在服务调用方配置mock参数:
<dubbo:reference id="xxxService" interface="com.x..service.xxxxService" check="false" mock="true" />说明:配置了mock参数之后,假设在调用服务的时候,远程服务没有启动,或者各种网络异常了,那远程服务
会去寻找自定义的mock业务处理类进行业务处理。
因此还需配置一个自定义mock业务处理类
在接口服务xxxxService的目录下创建相应的mock业务处理类,同时实现业务接口xxxxService(),接口名要
注意命名规范:接口名+Mock后缀,mock实现需要保证有无参的构造方法。
public class xxxxServiceMock implements xxxxService {@Overridepublic String getXXXX(int id) {return "this is exception 自定义....";}
}
Dubbo服务降级设置_击水三千里的专栏-CSDN博客
7.dubbo怎么优雅停机
对于通过dubbo Main方式启动dubbo容器的,只需在启动前添加java环境变量-Ddubbo.shutdown.hook=true即
可。
对于tomcat等web容器方式启动dubbo的,需要在关闭时执行方法ProtocolConfig.destroyAll();kill -9 默认对于dubbo Main方式启动的,优雅停机不生效
8.dubbo怎么自动dump线程池
dubbo 内部有几种线程模型,都是使用 java 线程池实现的,任务被拒绝后会输出堆栈信息。我们可以看它是怎
么实现的
Dubbo 源码分析之自定义线程池拒绝策略(使用 Java 程序 Dump 线程堆栈信息)_苏苏爱自由-CSDN博客
9.Dubbo 的原理
10.dubbo如何一条链接并发多个调用
11.Dubbo的网络协议是什么
1dubbo协议Dubbo缺省协议采用单一长连接和NIO异步通讯,使用基于于netty+hessian(序列化方式)交互。适合于小数据
量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。Dubbo缺省协议不适合传送大数据
量的服务,比如传文件,传视频等,除非请求量很低。2 rmi协议走java二进制序列化,多个短连接,适合消费者和提供者数量差不多,适用于文件的传输,一般较少用,在大数据
量下的传输性,建议使用rmi协议3 hessian协议走hessian序列化协议,多个短连接,适用于提供者数量比消费者数量还多,适用于文件的传输,一般较少用4 http协议走json序列化5 webservice
12.讲下Dubbo服务注册与发现的基本流程
13.Dubbo有台节点挂了是怎么处理的
14.dubbo的优缺点是什么
15.dubbo怎么修改失败重试次数,默认是多少
16.dubbo内部机制,数据怎么流转的
17.dubbo遇到那些坑
18.Dubbo运行时,突然所有的zookeeper全部宕机,Dubbo是否还会继续提供服务
可以,因为刚开始初始化的时候,消费者会将提供者的地址等信息拉取到本地缓存,所以注册中心挂了可以继续通信
19.Dubbo源码
20.Dubbo使用zookeeper注册中心
21.Dubbo怎么使用redis作为注册中心
<dubbo:registry address="redis://127.0.0.1:6379" username="admin" password="password"></dubbo:registry>
<dubbo:protocol name="dubbo" port="20880" />
22.Dubbo最佳实践
23.Dubbo调优经验
Zookeeper
1、Zookeeper选举的原理
ZooKeeper的选举算法是基于ZAB协议(ZooKeeper Atomic Broadcast)。ZooKeeper的选举算法有两种,一种是FastLeaderElection(快速选举),另一种是LeaderElection(经典选举)。默认是FastLeaderElection
FastLeaderElection(快速选举): FastLeaderElection是ZooKeeper的默认选举策略,它通过在选举过程中引入了逻辑时钟(ZXID),以此来减少选举过程中的网络通信次数,提高选举速度。 在FastLeaderElection过程中,每个节点将自己的状态(包括ZXID、选举轮数、节点ID等)打包成投票信息,并广播给其他所有节点。每个节点在收到投票信息后,将对方的投票信息和自己的进行比较,如果对方的ZXID、选举轮数、节点ID等都大于自己的,那么就会更新自己的投票信息,并广播给其他所有节点。这样,经过若干轮的投票,最终会有一个节点的投票信息得到超过半数的认可,这个节点就会成为Leader。
LeaderElection(经典选举): LeaderElection是ZooKeeper的早期选举策略,它的选举过程比较简单,但需要多次网络通信,效率较低。 在LeaderElection过程中,每个节点都会首先将自己设置为LOOKING状态,并向其他所有节点发送投票信息。收到投票信息的节点,如果发现自己的状态为LOOKING,并且对方的节点ID大于自己的,那么就会回复一个OK信息。如果一个节点收到了超过半数的OK信息,那么这个节点就会成为Leader。
在实际应用中,由于FastLeaderElection的选举效率更高,因此被设置为了ZooKeeper的默认选举算法。
2、Zab的原理
ZAB(ZooKeeper Atomic Broadcast)协议是ZooKeeper用来保证分布式一致性的重要协议,其主要包括以下三个阶段:
发现(Discovery):这个阶段主要是由Leader节点向Follower节点广播消息,即Zab的消息广播阶段。Leader节点将消息以proposal的形式发送给其他所有的Follower节点。当Follower节点接收到proposal消息后,会返回ack消息。
同步(Synchronization):这个阶段主要是为了让Leader节点和Follower节点的数据达到一致。在ZooKeeper启动或者在Leader节点崩溃后选举新的Leader节点的过程中,会执行这个阶段。
广播(Broadcast):当所有服务器都处于同步状态时,Leader就可以开始广播消息(事务提案)。Follower收到消息后返回应答,一旦Leader发现大多数Follower都返回了应答,就认为消息广播成功。
Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。
为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。
epoch:可以理解为当前集群所处的年代或者周期,每个 leader 就像皇帝,都有自己的年号,所以每次改朝换代, leader 变更之后,都会在前一个年代的基础上加 1。这样就算旧的 leader 崩溃恢复之后,也没有人听他的了,因为 follower 只听从当前年代的 leader 的命令。
3、Zookeeper的角色
- ZooKeeper的Watch机制,也被称为观察者模式,是一种当指定节点的数据发生改变时,能够通知客户端的机制。
- 当客户端在读取一个znode节点数据时,可以选择设置一个watch,那么当这个znode的数据发生变化或者被删除时,ZooKeeper会向设置了watch的客户端发送一个通知,告诉客户端该节点的数据已经发生了改变。此外,如果客户端设置了watch的节点所在的路径被删除,客户端也会收到一个通知。
- 这种机制可以使客户端及时得知节点数据的变化,从而进行相应的处理,比如更新本地缓存,触发某个业务逻辑等。
- 值得注意的是,ZooKeeper的watch机制是一次性的,也就是说一旦触发后,如果客户端还想继续保持对该节点的watch,需要再次对节点进行操作并设置watch。
ZooKeeper的watch事件包括:
- NodeCreated:当节点被创建时触发
- NodeDeleted:当节点被删除时触发
- NodeDataChanged:当节点数据发生变化时触发
- NodeChildrenChanged:当子节点列表发生变化时触发这就是ZooKeeper的Watch机制原理,它是ZooKeeper实现分布式协调服务的重要功能之一。
5、zookeeper使用场景
(1)配置管理
(2)集群管理(3)分布式锁
Mybatis
1.Mybatis的底层实现原理
1)读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
2)加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
3)构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
4)创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
5)Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
6)MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
7)输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
8)输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。
2.mapper接口调用的原理
mybatis的mapper代理是用的jdk的动态代理
3.hibernate和mybatis最本质的区别
Hibernate更注重对象模型的抽象,提供了更高级别的封装;而MyBatis更注重SQL语句的灵活性和可控性。根据项目需求和开发者的喜好,可以选择合适的框架来使用。
4、Mybatis中#和$的区别
#
:#{}
会将参数值做为一个预编译参数处理,用于防止SQL注入。例如select * from user where id = #{id}
,MyBatis在执行SQL语句前,会把#{id}
替换为?
,然后通过PreparedStatement的set方法设置参数。
$
:${}
则是将参数直接拼接到SQL语句中,所以它有SQL注入的风险。例如select * from ${tableName}
,MyBatis在执行SQL语句前,会直接将${tableName}
替换为参数值。
5.Mybatis缓存
- 当Spring框架启动时,它会创建一个SqlSessionFactoryBean,这个Bean负责创建SqlSessionFactory。SqlSessionFactory是创建SqlSession的工厂,每个SqlSession代表一个数据库连接会话。
- 当有数据库操作需要进行时,Spring会从SqlSessionFactory中获取一个SqlSession。在获取SqlSession时,SqlSessionFactory实际上是从Druid数据连接池中获取一个数据库连接。
MyBatis 默认开启了一级缓存,一级缓存是在SqlSession 层面进行缓存的。即同一个SqlSession ,多次调用同一个Mapper和同一个方法的同一个参数,只会进行一次数据库查询,然后把数据缓存到缓冲中,以后直接先从缓存中取出数据,不会直接去查数据库。
但是不同的SqlSession对象,不同的SqlSession都是相互隔离的,所以相同的Mapper、参数和方法,他还是会再次发送到SQL到数据库去执行,返回结果。为了克服这个问题,需要开启二级缓存,SqlSessionFactory层面给各个SqlSession 对象共享。默认二级缓存是不开启的,需要手动进行配置二级缓存的范围是 mapper 级别( mapper 同一个命名空间), mapper 以命名空间为单位创建缓存数据结构,结构是 map。 mybatis 的二级缓存是通过 CacheExecutor 实现的。 CacheExecutor其实是 Executor 的代理对象。具体的缓存实现可以由用户自定义,如使用Ehcache或Redis等
Elasticsearch
1.说说你们公司 es 的集群架构,索引数据大小,分片有多少
按数据库划分索引。十几个库对应十几个索引,每个索引几百G,5个分片,每个分片几十G。3个副本。
2.elasticsearch怎么调优
1.关闭data结点的http功能
2.最小主节点数量,防止脑裂
3.指定集群及节点名字
4.一台服务器上最好只部署一个Node
5.预留一半内存给Lucene使用
6.集群恢复配置
7.索引别名和零停机
8.设置最优的分片数和备份数
9.设置合理的刷新时间
10.根据业务设置合理的字段类型
Elasticsearch 原理和使用总结_击水三千里的专栏-CSDN博客_elasticsearch原理及使用
3.elasticsearch 的倒排索引是什么
4.elasticsearch 索引数据多了怎么办
尽可能使用基于时间的索引来管理数据保留期。根据保留期(retention period,可以理解成有效期)将数据分
组。基于时间的索引还可以轻松地随时间改变主分片和副本分片的数量(以为要生成的下一个索引进行更改)。这
简化了适应不断变化的数据量和需求。
Kafka
深入探究Kafka:高吞吐量与低延迟背后的技术原理_kafka页缓存-CSDN博客
SpringBoot
1、SpringBoot核心功能
2、SpringBoot优缺点
3、SpringBoot几个常用的注解
4、springboot启动机制
Spring Boot 的启动过程包括以下步骤:
- 创建SpringApplication对象:SpringApplication是Spring Boot框架的入口类,用于启动嵌入式的Servlet容器(如Tomcat)并初始化Spring应用上下文。当你运行Spring Boot应用的main方法时,实际上是在创建并运行SpringApplication对象。
- 运行SpringApplication:通过调用SpringApplication的run方法启动Spring Boot应用。这个方法首先会创建一个Spring应用上下文(ApplicationContext)。ApplicationContext是Spring框架的核心部分,负责管理Bean(对象)的生命周期和配置。
- 加载配置文件:Spring Boot应用会自动加载application.properties或application.yml配置文件,并将配置属性绑定到Spring Environment中。你可以在Spring Boot应用中通过@Value注解或Environment对象获取配置属性。
- 执行CommandLineRunner和ApplicationRunner:如果你的Spring Boot应用实现了CommandLineRunner或ApplicationRunner接口,那么在Spring Boot应用启动后,run方法会被自动调用。
- 启动内嵌的Servlet容器:如果你的Spring Boot应用是一个Web应用,那么Spring Boot会自动启动一个内嵌的Servlet容器(默认是Tomcat)。你可以通过配置属性server.port设置端口号。
- 应用启动完成:当Spring Boot应用启动完成后,你可以通过浏览器访问你的Web应用,或者通过命令行调用你的命令行应用。
5、spring boot自动配置是怎么实现的
- Spring Boot具有一个叫做"自动配置"的特性,它会根据你的classpath和已有的配置来自动配置Spring应用。这是通过在Spring Boot启动时扫描项目的classpath,然后根据classpath中的类来决定应该启用哪些自动配置。
- 例如,如果Spring Boot在classpath中发现了Spring MVC的类(如DispatcherServlet),那么它会推断出你正在开发一个Web应用,于是它会自动配置Spring MVC。
- 具体来说,Spring Boot会为你创建并注册一个DispatcherServlet(处理HTTP请求的主要Servlet),并且配置一些默认的MVC组件,如:MessageConverter(用于转换请求和响应中的数据),ViewResolver(用于解析视图名到具体的视图对象)等等。
- 而你只需要在Spring Boot的主配置类上添加@EnableAutoConfiguration或者@SpringBootApplication(它包含了@EnableAutoConfiguration)注解就可以启用这个自动配置功能。
6、springboot知识点整理
SpringCloud
1.简单说下你对Spring Cloud的理解
2.spring cloud常用组件及其作用是什么
服务发现——Netflix Eureka
客服端负载均衡——Netflix Ribbon
断路器——Netflix Hystrix
服务网关——Netflix Zuul
分布式配置——Spring Cloud Config
架构
系统架构
1.讲讲redlock算法实现,争议在哪里
2.如果有人恶意创建非法连接,怎么解决
3.一个在线文档系统,文档可以被编辑,如何防止多人同时对同一份文档进行编辑更新
在一个在线文档系统中,防止多人同时编辑同一份文档的问题,通常可以通过以下几种方式来解决:
- 悲观锁:当用户开始编辑文档时,系统会先锁定该文档,其他用户此时不能对该文档进行编辑,直到编辑者完成编辑并提交改动,系统才会释放该文档的锁。这通常可以通过在数据库中为文档添加一个锁定状态字段来实现。
- 乐观锁:在乐观锁策略中,系统允许多个用户同时编辑同一份文档,但在保存修改时,会检查文档是否在编辑过程中被其他人修改过。具体的做法是在文档中添加一个版本号字段,每次文档被修改时,版本号都会自动加一。当用户保存修改时,系统会检查该用户最后一次读取文档时的版本号是否与当前的版本号相同,如果不同,说明文档在编辑过程中已被他人修改,此时系统会拒绝保存该用户的修改。
- 实时协同编辑:这是一种更复杂的解决方案,允许多个用户实时地同时编辑同一份文档,如Google Docs。这通常需要使用一些复杂的算法(如操作转换(OT)或冲突无关状态(CRDT))来确保所有用户看到的都是一致的文档状态。
- 基于队列的编辑请求:在这种策略中,所有对文档的编辑请求都会被放入一个队列,然后依次处理。这种方法的好处是避免了并发编辑导致的冲突,但可能会引入一定的延迟。
这几种方法各有优缺点,选择哪种方式取决于应用的具体需求。
4.线上系统突然变得异常缓慢,你如何查找问题
5.异步模式的用途和意义
6.如何设计一套高并发支付方案,架构如何设计
7.聊下曾经参与设计的服务器架构并画图,谈谈遇到的问题,怎么解决的
8.应用服务器怎么监控性能,各种方式的区别
9.如何实现负载均衡,有哪些算法可以实现
10.设计一个可以控制缓存总体大小的自动适应的本地缓存
设计一个可以控制缓存总体大小的自动适应的本地缓存需要考虑以下几个要素:
- 缓存淘汰策略:决定何时以及如何替换或删除缓存中的条目。常见的缓存淘汰策略包括最近最少使用(LRU)、最不经常使用(LFU)、先进先出(FIFO)等。
- 缓存大小:需要设置一个缓存的最大大小,当达到这个大小时,需要使用淘汰策略来移除一些条目。
- 自动适应:缓存需要能够根据系统的运行状况进行自动的调整。例如,当系统内存紧张时,缓存可以自动减小其大小;当系统内存充足时,可以自动增大其大小。
以下是一个简单的设计:
- 使用哈希表存储缓存条目,以支持快速的查找和更新。
- 使用一个双向链表(或优先级队列)来记录缓存条目的访问顺序或频率,以支持淘汰策略。
- 设置一个最大缓存大小。每次添加新的缓存条目时,先检查当前的缓存大小是否已达到最大值。如果是,则根据淘汰策略移除一些条目,再添加新的条目。
- 通过监听系统的内存使用情况,动态调整缓存的大小。例如,可以设置一个内存使用阈值,当系统的内存使用超过这个阈值时,自动减小缓存的大小;当内存使用低于这个阈值时,自动增大缓存的大小。
- 还可以考虑将一些不常用但需要保留的数据写入到磁盘,以节省内存空间。
11.后台系统怎么防止请求重复提交
12.描述一个服务从发布到被消费的详细过程
13.如何优化线程数
14.常见的缓存策略有哪些,如何做到缓存与 DB 里的数据一致性,你们项目中用到了什么缓存系统,如何设计的
15.缓存数据过期后的更新如何设计
缓存数据过期后的更新,主要有以下几种常用的设计策略:
- 惰性删除(Lazy Deletion):当有请求查询数据时,先检查该数据是否过期,如果过期则从源头(如数据库)重新获取数据,更新缓存并返回数据。这种方式的优点是简单,不会消耗无谓的性能去维护那些永远不会被访问到的过期数据。缺点是如果数据源获取数据的代价很高,可能会导致请求等待时间过长。
- 定时删除(Time-based Deletion):通过定时任务定期扫描缓存,删除过期的数据。并且在定时任务中,预先加载可能会被访问的数据,更新到缓存中。这种方式可以保证大部分情况下请求能够快速从缓存获取数据,但是可能会消耗一些额外的性能去处理那些实际上并不会被访问到的数据。
- 通知删除(Notification-based):如果源头数据支持发布订阅模式,那么可以在数据发生改变时,发布一个消息,缓存订阅这个消息,并进行相应的处理(删除或更新缓存)。这种方式的优点是能够及时更新数据,缺点是需要源头数据支持发布订阅模式,实现相对复杂。
以上三种策略可以根据实际业务需求,甚至可以混合使用。例如,结合使用惰性删除和定时删除,既保证了缓存数据的更新,也避免了不必要的性能消耗。
16.有一个订单系统,订单日增加1000万,怎么设计表
分布式、高并发
1.如果设计高可用高并发系统
2.分布式事务的原理,优缺点,如何使用分布式事务
3.分布式当中如何避免对同一条修改的冲突?
4.接口限流怎么做
单机限流和分布式应用限流_击水三千里的专栏-CSDN博客_分布式限流和单机限流
5.一次RPC请求的流程是什么
一个基本的RPC架构里面应该至少包含以下4个组件:1、客户端(Client):服务调用方(服务消费者)2、客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端3、服务端存根(Server Stub):接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理4、服务端(Server):服务的真正提供者
6、自己实现过rpc么,原理可以简单讲讲,Rpc要解决什么问题
1、RPC是什么
RPC远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。简言之,RPC使得程序能够像访问本地系统资源一样,去访问远端系统资源。2.RPC要解决什么问题
各个团队的服务提供方就不要各自实现一套序列化、反序列化、网络框架、连接池、收发线程、超时处理、状态机等“业务之外”的重复技术劳动,造成整体的低效。
看了这篇Dubbo RPC面试题,让天下没有难面的面试题!
7、高并发下1个数频繁更改(更改频率1000次-10000次)应该怎么处理?
在高并发场景下,一个数值频繁更改(如1000次-10000次)的情况,可能会引发线程安全的问题,可能会出现读写冲突,甚至可能导致数据不一致。以下是一些处理策略:
- 使用原子操作:例如在Java中的AtomicInteger,或者在C++中的std::atomic,这些都是线程安全的,可以保证在并发情况下的原子性。
- 使用锁机制:包括互斥锁、读写锁等,可以保证在任何时刻只有一个线程能够访问到共享数据,避免并发冲突。但是过度依赖锁机制可能会导致性能下降。
- 使用乐观锁:乐观锁是一种无锁的设计,通过比较数据版本来决定数据是否可以被更改。如果数据版本没有发生改变,那么就可以执行更改操作。否则,可能需要重新读取数据或者进行其他的冲突解决操作。
- 分片:将频繁更改的数值拆分到不同的服务器节点上,每个节点只处理一部分的更改操作,从而降低单个节点的并发压力。
- 缓冲:引入一个缓冲区,将频繁的更改操作先写入缓冲区,然后再将缓冲区的数据批量写入到目标数值中。这样可以降低对目标数值的并发访问压力。
6. 使用队列:引入一个队列,将更改操作放入队列,再由一个或多个后台线程去处理队列中的操作。这样也可以降低对目标数值的并发访问压力。
8.分布式服务调用方,不依赖服务提供方的话,怎么处理服务方挂掉后,大量无效资源请求的浪费,如果只是服务提供方吞吐不高的时候该怎么做,如果服务挂了,那么一会重启,该怎么做到最小的资源浪费,流量半开的实现机制是什么
在分布式系统中,服务调用方(也称为客户端)不应该完全依赖服务提供方(也称为服务器)。当服务提供方出现故障或者响应慢时,应该有策略去处理这种情况,以避免资源的浪费和系统的瘫痪。
以下是一些常见的处理策略:
- 超时设置:为每个服务调用设置一个合理的超时时间,如果在这个时间内没有收到服务提供方的响应,那么就认为这个服务调用失败,进而可以进行重试或者返回错误信息。
- 重试策略:在服务调用失败后,可以立即进行重试。但是重试的次数和频率需要有一定的限制,否则可能会加重服务提供方的负载,甚至可能导致服务提供方的崩溃。
- 熔断机制:当服务提供方的错误率超过一定的阈值时,可以触发熔断机制,直接拒绝新的服务调用,而不是将它们转发给服务提供方。这样可以保护服务提供方,防止其过载。
- 降级处理:在服务提供方不能正常提供服务时,可以返回一个预设的、较为简单的响应,或者从缓存中获取数据,以此来提高系统的可用性。
- 流量半开机制:当服务提供方从熔断状态恢复后,不应该立即将全部流量引入,而是应该先开启一小部分的流量(也就是“半开”状态),观察服务提供方的运行状况。如果服务提供方能够正常处理这部分流量,那么就可以逐渐增加流量;如果服务提供方仍然不能正常处理,那么就应该再次触发熔断。
以上策略可以根据实际的业务需求和环境进行选择和调整。在实现这些策略时,可以使用一些成熟的服务框架和库,例如Netflix的Hystrix,它提供了熔断、降级、隔离等功能。
系统设计、设计模式
- 常见设计原则
- 编程中自己都怎么考虑一些设计原则的,比如开闭原则,以及在工作中的应用
- OO 的设计原则
- 请结合 OO 设计理念,谈谈访问修饰符 public、private、protected、default 在应用设计中的作用
- 如何做到接口的幂等性
- 说说你平时用到的设计模式
- 工厂模式
- 代理模式
- 适配模式
性能优化
- 线上系统突然变得异常缓慢,你如何查找问题
- 讲下你做过那些性能优化的措施
微服务
1、讲下你理解的微服务?
2、微服务(RPC)与SOA的区别是什么?
1、REST可以看着是HTTP协议的一种直接应用,默认基于JSON作为传输格式,使用简单,学习成本低效率高,但是安全性较
低。2、SOAPSOAP是一种数据交换协议规范,是一种简单的、基于XML的协议的规范。而SOAP可以看着是一个重量级
的协议,基于XML、SOAP在安全方面是通过使用XML-Security和XML-Signature两个规范组成了WS-Security
来实现安全控制的,当前已经得到了各个厂商的支持 。它有什么优点?简单总结为:易用、灵活、跨语言、跨平台。3、SOA面向服务架构,它可以根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署、组合和使用。服务层是
SOA的基础,可以直接被应用调用,从而有效控制系统中与软件代理交互的人为依赖性。SOA是一种粗粒度、松耦合服务架构,服务之间通过简单、精确定义接口进行通讯,不涉及底层编程接口和通讯
模型。SOA可以看作是B/S模型、XML(标准通用标记语言的子集)/Web Service技术之后的自然延伸。4、REST 和 SOAP、RPC 有何区别呢?没什么太大区别,他们的本质都是提供可支持分布式的基础服务,最大的区别在于他们各自的的特点所带来的不
同应用场景 。
3、微服务的模块怎么划分?
消息队列
- MQ的基本原理
- MQ消息可靠性保证
- MQ怎么解决幂等性
- MQ系统的数据如何保证不丢失
- 用过哪些 MQ,和其他 mq 比较有什么优缺点
- 用过哪些 MQ,和其他 mq 比较有什么优缺点,MQ 的连接是线程安全的吗
- 怎么利用 mq 实现最终一致性
- MQ有可能发生重复消费,如何避免,如何做到幂等
- MQ 的消息延迟了怎么处理,消息可以设置过期时间么,过期了你们一般怎么处理
- 消息队列满了怎么处理
- 如何自己设计一个消息中间件
- kafka,activemq,rabbitmq本质区别是啥
数据库
关系型数据库
1.数据库隔离级别有哪些,各自的含义是什么,MYSQL默认的隔离级别是是什么
2.不可重复读与幻读的区别
MySQL的性能优化理论_default options are read from the following files -CSDN博客
3.MYSQL有哪些存储引擎,各自优缺点,myisam与innodb区别
4.乐观锁和悲观锁是什么,以及各自的优略势
5.INNODB的标准行级锁有哪2种,解释其含义
InnoDB的行级锁有两种类型,分别是共享锁(S锁)和排他锁(X锁)。
- 共享锁(S锁):也被称为读锁。如果一个事务对一行数据加了共享锁,那么其他的事务也可以对这一行数据加共享锁,也就是说多个事务可以同时读取这一行数据。但是,在这一行数据上加了共享锁的情况下,其他事务是不能加排他锁(也就是不能进行写操作)的。共享锁的主要作用是防止当一个事务在读取数据时,其他事务对这个数据进行修改。
- 排他锁(X锁):也被称为写锁。如果一个事务对一行数据加了排他锁,那么其他的事务不能对这一行数据加任何锁,也就是说不能对这一行数据进行读取和写入。排他锁的主要作用是防止当一个事务在修改数据时,其他事务对这个数据进行读取或者修改。
6.SQL优化的一般步骤是什么
7.怎么看执行计划,如何理解其中各个字段的含义
8.数据库会死锁吗,举一个死锁的例子,mysql怎么解决死锁
9.Mysql的索引原理
10.Mysql的索引数据结构
11.Mysql多列索引(复合索引)的生效规则
12.索引的类型有哪些,如何创建合理的索引,索引如何优化
13.聚集索引和非聚集索引的区别,怎么存储的
14.select for update是什么含义,会锁表还是锁行或是其他
SELECT FOR UPDATE
会给查询出来的所有行加上排它锁(或写锁)。如果这些行在当前事务处理结束前被另一个事务所请求,那么请求的事务会被阻塞,直到当前事务完成。SELECT FOR UPDATE
通常不会锁整张表,只会锁定被选中的行。但是,在一些情况下,例如当表的索引结构无法支持行级锁定(行级锁),或者数据库的隔离级别被设置得比较高时,SELECT FOR UPDATE
可能会锁定更大范围的数据,甚至锁定整张表。
15.Btree原理
B树(B-Tree)是一种自平衡的、可以存储大量数据的树形数据结构。它是一种多路搜索树,可以有多于两个子节点。
B树的特点:
- 所有叶节点都在同一层。
- 每个节点包含n个键,按升序排列,每个键分割出n+1个子树。
- 每个节点中的键都对应一个关联的子树,子树的所有键都小于或等于它的父节点,同时也大于或等于它前面的节点(如果存在)。
- B树的根要么是叶节点,要么其子节点至少有2个。
- 除了根节点以外的所有节点(内部节点)都有k个子节点,其中 [m/2] <= k <= m (m为树的阶,[x]表示不大于x的最大整数)。
B树的操作原理:
查找操作:从根节点开始,对每一个节点,使用二分查找法定位到关键字的位置。如果找到了关键字,那么就返回。否则,跟进到关键字对应的子树,继续查找。这个过程是递归的。
插入操作:首先查找到关键字应当插入的位置,然后在该位置插入关键字。如果插入后节点的关键字数量超过了m,那么就将节点分裂为两个。
删除操作:首先查找到关键字位置,然后删除关键字。如果删除后节点的关键字数量少于[m/2],那么就需要调整节点,保持B树的性质。
B树与二叉树相比,优点是由于数据能够在一个节点内存储多个值,并且树的度(m)较高,因此B树的高度相对较低,减少了查询的IO次数,提高了查询速度,特别适合于磁盘等大容量存储设备的数据存储和查找。
16、Btree怎么分裂的,什么时候分裂,为什么是平衡的
B+树分裂过程:当一个节点满了,即节点中的键值对数量达到了最大限度,此时需要进行分裂。分裂过程中,首先将所有键值对(包括新插入的键值对)排序,然后选择中间位置的键作为上升键。上升键以上的键值对放入新的节点,上升键保留在原节点,上升键将原节点和新节点链接,同时上升键还要插入到其父节点中。如果父节点也满了,就继续分裂,直到某个父节点未满或者新建一个根节点,这样保证了B+树的高度一直是平衡的。
何时分裂:当一个节点中的键值对数量达到了最大限度(设定的度数)并且此时有新的键值对需要插入时,就需要进行分裂。
为何是平衡的:B+树之所以是平衡的,是因为它的所有叶子节点都在同一层,并且当节点满了需要分裂时,是平均分配键值对到新的节点中,并且分裂可能会上溯到根节点,甚至导致根节点分裂,从而增加树的高度。这样无论在哪个地方插入键值对,都能保证所有路径从根节点到叶子节点的长度相同,即保证了平衡性。
B+Tree - 《零基础到全栈系列》_哔哩哔哩_bilibili
17、说下mysql架构是怎样的
18.某个表有近千万数据,CRUD比较慢,如何优化
19.Mysql怎么优化tablescan
20.mysql中in和exists区别
21.数据库自增主键可能的问题
23.怎么解决MYSQL的主从同步延迟
24.你做过的项目里遇到分库分表了吗,怎么做的
25.sharding jdbc的原理知道么
26、sharding jdbc怎么生成唯一主键
雪花算法
27、sharding jdbc架构
28、MySQL事务实现原理,ACID实现原理
MySQL通过使用存储引擎如InnoDB来支持事务处理。事务处理的实现原理主要涉及到ACID四个属性的实现:
- 原子性(Atomicity):原子性是指事务中的所有操作,要么全部成功,要么全部失败回滚,这和MySQL中的事务提交或者事务回滚是一致的。在MySQL中,如果一组SQL语句在一个事务中,只有当所有SQL语句都执行成功,该事务才会提交。只要其中有任何一个SQL语句执行失败,就会触发事务的回滚操作。通过Undo log(回滚日志)和事务控制语句实现的
- 一致性(Consistency):一致性是指事务应确保数据库的状态从一个一致状态转变为另一个一致状态。MySQL使用了一种叫做"undo log"的机制来保证这一点。当一个事务进行中时,MySQL会记录下修改的旧值,如果事务失败需要回滚,就可以利用这个undo log将数据恢复到原来的状态。
- 隔离性(Isolation):隔离性是指并发的事务之间不会互相影响,保证每个事务在不受外部并发事务影响的“独立”环境执行。MySQL使用了多版本并发控制(MVCC)和锁定机制来实现隔离性。根据事务的隔离级别的不同,MySQL可以提供读未提交、读已提交、可重复读和串行化等多种隔离级别。
- 持久性(Durability):持久性是指一旦事务提交,那么这个事务对数据库的改变就是永久的。即使在系统崩溃的情况下,数据库也能够保持数据的一致性。MySQL通过redo log(重做日志)来实现持久性。当一个事务提交时,MySQL会先将修改记录到redo log,并更新内存中的数据。即使此时发生崩溃,MySQL也能通过redo log来恢复数据。
因此,使用InnoDB等支持事务的存储引擎,MySQL可以很好地支持ACID事务特性。
深入探索 MySQL Binlog:原理、工作机制与实践应用_mysql binlog原理-CSDN博客
29.说下mysql内存分配
MySQL的内存分配可以根据其用途大致划分为以下几个主要部分:
- 全局内存:这部分内存是由MySQL服务器在启动时分配的,它包括了各种全局缓冲区和缓存。例如,InnoDB存储引擎的缓冲池(InnoDB Buffer Pool),它是用于缓存表数据和索引的内存区域;键缓存(Key Cache),用于MyISAM存储引擎的索引缓存;查询缓存(Query Cache),用于缓存SELECT语句的结果等。
- 会话内存:每当一个客户端连接到MySQL服务器时,都会为这个会话分配一些内存,这部分内存用于存储与该会话相关的数据,例如连接缓冲区、结果集缓冲区、排序缓冲区等。这些内存在会话结束时会被释放。
- 临时内存:MySQL在执行某些操作时,可能需要使用临时表来存储中间结果,这时就会使用到临时内存。例如,当执行排序或者组合操作时,MySQL可能会创建一个内存临时表。如果临时表的大小超过了max_heap_table_size的值,MySQL会将临时表转移到磁盘上。
- 线程堆栈:每个连接MySQL服务器的线程都有一个线程堆栈,用于存储本地变量、函数调用等信息。其大小由thread_stack系统变量控制。
以上就是MySQL的主要内存分配情况,实际上MySQL的内存管理机制还包括很多细节,会受到MySQL的配置、所使用的存储引擎、查询的复杂度等多种因素的影响。
30、update语句的底层实现是怎样的
解析:MySQL的解析器将Update语句解析为一个数据结构,以供优化器使用。
优化:MySQL的优化器决定如何最有效地执行Update语句。它可能会选择使用哪些索引,以及如何连接表等。优化器还会计划在执行过程中需要获取的锁。
锁定:在执行Update语句之前,MySQL会获取必要的锁。这通常包括行级锁以确保数据的一致性。
执行:MySQL将根据优化器的计划,执行Update语句。这通常包括查找需要更新的行,修改这些行中的数据,并将修改写入到undo日志中,以便在事务回滚时可以恢复数据。
提交:如果Update语句在事务中,并且这个事务已经完成,那么MySQL会提交事务。提交事务意味着将数据的修改永久地保存到磁盘上。
解锁:提交事务后,MySQL会释放在执行过程中获取的所有锁。
31、mysql行级锁一定会锁定指定行么
如果一个查询无法使用索引,并且需要扫描全表的数据,那么MySQL可能会使用表锁而不是行级锁。
非关系型数据库
1Redis的数据结构都有哪些
2redis的string结构相关的操作
3redis的list结构相关的操作
4redis的hash结构相关的操作
5讲讲持久化方式 ,aof和rdb的区别
6redis2和redis3的区别
前者是一个完全无中心的设计,节点之间通过gossip方式传递集群信息,数据保证最终一致性
后者是一个中心化的方案设计,通过类似一个分布式锁服务来保证强一致性,数据写入先写内存和redo log,然后定期compat归并到磁盘上,将随机写优化为顺序写,提高写入性能。
7redis3内部通讯机制
8redis主键失效原理
9redis集群有哪些玩法,各自优缺点,场景
10Redis的Sentinel实现机制与原理
11redis的集群怎么同步的数据的
12redis的主从复制的原理
13Redis集群怎么扩容
14什么是Redis集群的分库和分片
15Redis的并发竞争问题如何解决,了解Redis事务的CAS操作吗
16Redis的选举算法和流程是怎样的
17知道哪些redis的优化操作
18redis常见性能问题和解决方案
19Redis的底层原理
20Redis的线程模型是什么
21Redis为什么使用单进程单线程方式也这么快
22Redis的缓存策略和主键失效机制
23Redis的回收策略
24Redis相比memcached有哪些优势
25Redis的回收策略
26Memcache的原理,哪些数据适合放在缓存中
27redis和memcached的区别
28Redis 的七个原则
29分布式下,怎么保证redis和mysql数据一致性
30Redis使用场景
31使用Redis遇到那些问题、故障
算法
1、什么是一致性 hash算法?
2、说说你知道的几种hash算法
3、什么是二叉树算法
4、什么是红黑树算法
5、二叉树和二叉查找树的区别是什么?
6、手写各种排序算法,如冒泡、快速等
7、说出常见的负载均衡算法?
8、写出常见排序算法(快排、冒泡)
9、二分法查找
项目
项目开发
- 你们项目是怎么打包部署
- 项目出现乱码怎么解决
- 你们是怎么管理分支的,出现冲突怎么解决
- 项目中遇到那些难题挑战,怎么解决
- 线上出了问题怎么排查,分析的过程和方法
- 说说你最满意的一个项目的架构
- 画出你们项目的架构图
业务问题
1.分布式服务情况下,怎么保证一个新闻一天一个用户只能点赞一次
2.微信红包怎么实
这篇关于Java中高级面试题总览(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!