Java面试题汇总 真实面经总结(持续更新20240315)
2024-03-15 15:22:17 2 举报
AI智能生成
Java面试题汇总 真实面经总结(持续更新)超高质量 希望你可以认真对待
作者其他创作
大纲/内容
算法
无序数组中第K大的数
leetcode 92 反转链表中某一段
单词接龙。https://blog.csdn.net/weixin_47243236/article/details/122782684
无序数组中找出绝对值之和最小的两个数。(双指针)https://blog.csdn.net/qq_34465338/article/details/124365935
给定一字母和数字组合的字符串,求只包含最多一个字母任意多数字的最长字串。(滑动窗口)
leetcode 217. 存在重复元素
合并俩个有序链表
881救生艇 https://leetcode.cn/problems/boats-to-save-people/
lc 442. 数组中重复的数据
lc 11.盛水最多的容器
算法(无原题)- 求一个二维数组曼哈顿距离之和,时间复杂度小于O(n^2)
子主题
链表去重
判断字符串s2中是否包含字符串s1的排列之一
代码实现LRU,最近最少使用,删除最近最少使用的页
找出字符串中最长无重复字符的长度
给定一非零整数数组,要求正数在前,正数从大到小,负数从小到大排序
给定非零整数数组,正数在前,负数在后,不用关注顺序,要求空间复杂度O(1) 时间复杂度O(n)
堆的定义、调整过程及时间复杂度
单链表的快速排序
算法:验证对称二叉树
算法:一个数组里面有一个出现奇数次的数字和多个出现偶数次的数字 怎么快速找到这个奇数次的数字
算法:如何判断链表成环
算法题:字符串反转
找不重复子串的最长的字串
手撕代码: 最长回文子串 (最好知道多种解法、面试官在你写完后会问你有没有新的思路,口述一下即可)
算法题:两数之和
算法题堆积木
手撕: 由层序遍历构建二叉树
算法:三数之和(
手撕:字符串相加
力扣:x的平方根
中序遍历的写一个节点
项目
介绍下项目的主要功能、上下游系统整体架构
项目中说QPS有3W,如何防止一种类型的消息量较大从而影响其他消息
介绍下你的项目的作用、服务业务和整体架构
项目接口鉴权怎么做的?如何设计一个安全、健壮的鉴权逻辑?
你刚说到非对称加密,说一下非对称加密算法的逻辑?如何防止别人拿到公钥正常调用接口?
项目中如何做接口防刷的
介绍项目的主要功能、整体架构、技术选型以及选型原因
项目整体有多少个微服务,每个微服务部署了多少台
项目中的告警监控是如何做的
如果你们有流量的激增的场景,你会如何应对?
项目有接入链路追踪吗,都有哪些指标?具体实现原理有去了解吗,不同服务间的调用是如何传递 traceld 的,让你实现你会如何实现
详细讲一下项目中的乐观锁
用户登录使用threadLocal实现线程隔离
怎么实现的线程隔离?
怎么实现的线程隔离?
为什么ThreadLocal能让用户拿到自己的信
息?怎么设计的threadLocal
息?怎么设计的threadLocal
讲讲退出登录的时候对token的操作是什
么?
么?
Redis的使用,以及在项目重构中的作用缓存系统数据库之间的数据一致性解决
解决数据一致性问题时时效的保证
怎么做项目中相关使用Redis缓存的实体在编码时如何实现
使用本地缓存的原因
项目数据库建表细节
登陆这一块详细解释一下,token放在哪?是自定义的字段还是header自带的字段,为什么要用header白带的字段而不是自定义的字段,token在浏览器存在哪会不会存在服务端 服务器重启后token还在不在。
讲一下限流有什么具体的实现方案?公司的Redis架构是怎样的
项目具体功能实现逻辑
项目里为什么要用消息队列
请求很多,消息堆积处理不过来了如何应对(降级返回其他结果,集群)
计算机
大小端问题(疑似与 TCP\IP 协议有关)
数据从CPU执行存储的磁盘的全过程
常用负载均衡算法
RPC和 Http 有什么区别? RPC 为什么性能好?
http和https的区别
http请求的方式有哪些?(put、post、get、delete...)
计算机网络常见的分层是什么,TCPIP分别在哪一层
http协议常见状态码的含义 301 302 304 403 404 500 502 504
TCP三次握手具体的报文数据有哪些服务端发送ACK报文时确认号放在什么字段
tcp和udp的区别
输入一条url后发生了什么
如何在linux系统上查看所占空间最大的文件
ACK和ack number有什么区别
微服务
feign
底层原理 动态代理
服务发现和注册时如何实现的
SpringCloud 远程调用为啥要采用HTTP,而不是RPC
mybatis和mybatis-plus的区别
Springboot的优缺点
Spring starter
eureka
微服务启动后,会周期性地向Eureka Server发送心跳(默认周期为30秒) 以续约自己的信息
Eureka Server在一定时间内没有接收到某个微服务节点的心跳,Eureka Server将会注销该微服务节点(默认90秒)
Eureka Server在一定时间内没有接收到某个微服务节点的心跳,Eureka Server将会注销该微服务节点(默认90秒)
自我保护机制
默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务 与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制。
开放题
开放场景题:假设现在要维护用户的姓名和身份证号,从三高系统的角度你会如何设计存储和接口
开放场景题:现在给你一个服务器内存1T,磁盘、CPU 无限,初始有300G的uid,每天会更新150的uid,设计一个服务提供http的接口查询uid是否存在,从高性能、高可用、高并发的角度考虑整体架构设计。
场景题:如何内存处理大文件,统计出现次数最多的一个字符串
秒杀场景下扣减库存太慢了怎么办 (数据库集群,分库分表,索引优化,Redis热key,大key,Redis缓存过小
那我现在有10份数据,有1000个线程来争抢,你要怎么处理?
1 预先在优惠码系统生成5000万优惠码(每个优惠码21位字母数字串),每个优惠码可以去兑换中心兑换100元满减券;
2 设计一个发放优惠码的拉新运营活动,2月1号之后新注册用户有资格参与活动,活动期间每个用户只可以领取一个优惠码,每个优惠码智能发给一个用户;运营活动2月5号开始,2月6日结束,预计活动期间能将所有码都发放完毕;
3 运营活动交互形式:用户登录后,点击登录按钮,然后弹框提示“恭喜你获取100元满减券,优惠码是XXXXX”;
2 设计一个发放优惠码的拉新运营活动,2月1号之后新注册用户有资格参与活动,活动期间每个用户只可以领取一个优惠码,每个优惠码智能发给一个用户;运营活动2月5号开始,2月6日结束,预计活动期间能将所有码都发放完毕;
3 运营活动交互形式:用户登录后,点击登录按钮,然后弹框提示“恭喜你获取100元满减券,优惠码是XXXXX”;
金融热点账户
视频评论如何做
https://blog.51cto.com/u_15471709/4876580
https://blog.51cto.com/u_15471709/4876580
OTHER
你刚说到冷数据存TDB为什人要田TiDB,TiDB 有什么优缺点? 可以直接代替MySQL吗?
断点续传原理,如何实现
数据结构
链表增删快,那如何提高其查询效率有没有什么想法
B+树了解吗?B+树如何范围查询?
B+树退化的极端情况是什么
B+树退化的极端情况是什么
跳表了解吗?
大顶堆、小顶堆了解吗?
描述哈希算法
设计模式
策略模式下不同的类具体是怎么注入的
kafka
broker controller 选举过程
投票机制
java基础
值传递和引用传递的区别
值传递
值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递
所谓引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
值传递和引用传递都是传递的栈空间中的内容,因为栈空间存的是基本数据类型的值(所以表现为值传递),而栈空间存的引用类型的地址(所以表现为引用传递)。
值传递和引用传递最大的区别是传递的过程中有没有复制出一个副本来,如果是传递副本,那就是值传递,否则就是引用传递。
Java对象的传递,是通过复制的方式把引用关系传递了,因为有复制的过程,所以是值传递,只不过对于Java对象的传递,传递的内容是对象的引用。
Java对象的传递,是通过复制的方式把引用关系传递了,因为有复制的过程,所以是值传递,只不过对于Java对象的传递,传递的内容是对象的引用。
引用方式
强引用
默认就是强引用,任何一个对象的赋值操作就产生了对这个对象的强引用
软引用
软引用的意思是只有在内存不足的情况下,被引用的对象才会被回收
弱引用
引用的对象只要垃圾回收执行,就会被回收,而不管是否内存不足
虚引用
如果一个对象仅有虚引用,那么它就像没有任何引用一样,在任何时候都可能被 gc 回收。虚引用主要用来跟踪对象被垃圾回收的活动。
深拷贝 浅拷贝
浅拷贝
浅拷贝是指将一个对象复制到另一个变量中,但是只复制对象的地址,而不是对象本身。也就是说,原始对象和复制对象实际上是共享同一个内存地址的。因此,如果我们修改了复制对象中的属性或元素,原始对象中对应的属性或元素也会被修改。
BeanUtils类的拷贝对象
深拷贝
深拷贝是指将一个对象及其所有子对象都复制到另一个变量中,也就是说,它会创建一个全新的对象,并将原始对象中的所有属性或元素都复制到新的对象中。因此,如果我们修改复制对象中的属性或元素,原始对象中对应的属性或元素不会受到影响。
实现Cloneable接口,重新clone()方法
通过序列化和反序列化使⽤流进⾏深拷贝,注意类都要实现序列化接⼝
零拷贝
指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域。
NIO的零拷贝由transferTo()方法实现
transferTo方法调用触发DMA引擎将文件上下文信息拷贝到内核读缓冲区,接着内核将数据从内核缓冲区拷贝到与套接字相关联的缓冲区。DMA引擎将数据从内核套接字缓冲区传输到协议引擎(第三次数据拷贝)。
DMA
DMA的作用就是直接将IO设备的数据拷贝到内核缓冲区中。
MMP
就是将内核态和用户态的内存映射到一起,避免来回拷贝,实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上
sendfile
数据不经过用户态干预,直接将数据进行传输
sendfile + DMA Scatter/Gather
可以读page cache中的数据描述信息(内存地址和偏移量)记录到socket cache中,由 DMA 根据这些将数据从读缓冲区拷贝到网卡
direct I/O
硬件数据不经过内核态的空间,直接到用户态的内存中,这种方式就是Direct I/O。换句话说,Direct I/O不会经过内核态,而是用户态和设备的直接交互,用户态的写入就是直接写入到磁盘,不会再经过操作系统刷盘处理。
Stream List转map原理
使用Collectors.toMap
双亲委派
如何破坏
SPI
Tomcat
当一个类加载器试图加载一个类时,它首先会将这个请求委派给其父类加载器,只有在父类加载器无法加载这个类的情况下,才会尝试自己加载。这个过程会一直递归进行,直到达到Bootstrap ClassLoader,如果Bootstrap ClassLoader也无法加载该类,那么会抛出ClassNotFoundException。
Integer用==判断返回什么
比较它们的引用是否指向同一个对象
使用equals()方法可以比较Integer对象的内容而不是引用
如果超过128的数放在set可被去重吗
对于范围在-128到127之间的整数,无论你创建多少个Integer对象,它们都会引用池中的同一个对象。这意味着这些整数在Set中会被正确去重,因为它们的引用都相同
要去重这些超过范围的整数,你需要确保它们的值相同,并且重写了equals()方法和hashCode()方法以正确比较和去重这些对象。
long、double、float类型互转会丢精度吗
double/float到long会导致精度丢失
final关键字可以写在抽象类或接口上吗
不可以
final修饰的变量能再次修改吗
不可以
private修饰的属性,子类或父类能读到吗
private属性是类的私有成员,不能被子类或父类直接访问。
反射能获取private方法吗
可以
介绍下 Java 的异常体系,为什么 Java要区分检查异常和非检查异常
异常分为两种也就是:检查异常和非检查异常
①.检查异常/编译 :编译器要求必须处置的异常。当程序中可能出现这类异常,需要在编译时使用try-catch或者throws声明的异常,如果不处理将无法编译通过。IOException ClassNotFound
②非检查异常/ 运行时:在编译过程中没有任何问题,在java命令执行.class文件的时候,发生在运行期异常,在编译时不需要try-catch或者throws声明的异常,在程序运行期间会抛出。如NullPointerException、ArrayIndexOutOfBoundsException等。
①和②主要区别是处理方式不同,对于检查异常需要进行处理,非检查异常则不需要强制处理。而检查异常与非检查异常的区别则在于前者需要在编译时处理,而后者在程序运行时才会抛出。
①.检查异常/编译 :编译器要求必须处置的异常。当程序中可能出现这类异常,需要在编译时使用try-catch或者throws声明的异常,如果不处理将无法编译通过。IOException ClassNotFound
②非检查异常/ 运行时:在编译过程中没有任何问题,在java命令执行.class文件的时候,发生在运行期异常,在编译时不需要try-catch或者throws声明的异常,在程序运行期间会抛出。如NullPointerException、ArrayIndexOutOfBoundsException等。
①和②主要区别是处理方式不同,对于检查异常需要进行处理,非检查异常则不需要强制处理。而检查异常与非检查异常的区别则在于前者需要在编译时处理,而后者在程序运行时才会抛出。
解释NoClassDeffoundError出现的问题
线上应用是部署在物理机还是容器CPU 分配上有什么区别?
物理机部署:
独享硬件资源: 在物理机上部署应用,应用程序独享整个物理机的硬件资源,包括 CPU、内存、存储等。
定制化配置: 物理机上的应用可以根据硬件配置的不同进行定制化配置,可以根据实际需求选择更适合的硬件规格。
性能稳定: 由于应用独享硬件资源,性能通常更为稳定,而且不容易受到其他应用的影响。
容器部署:
虚拟化和隔离: 容器部署在物理机上,多个容器共享同一台物理机的资源。每个容器是独立隔离的,通过容器技术实现虚拟化,使得容器之间互不干扰。
轻量级: 容器相对于虚拟机来说更为轻量级,容器共享宿主机的操作系统内核,因此启动更快,占用更少资源。
灵活性和可移植性: 容器提供了更大的灵活性和可移植性,可以在不同环境中运行,保持一致的运行环境。这样,开发、测试和部署都更为简便。
虚拟化和隔离: 容器部署在物理机上,多个容器共享同一台物理机的资源。每个容器是独立隔离的,通过容器技术实现虚拟化,使得容器之间互不干扰。
轻量级: 容器相对于虚拟机来说更为轻量级,容器共享宿主机的操作系统内核,因此启动更快,占用更少资源。
灵活性和可移植性: 容器提供了更大的灵活性和可移植性,可以在不同环境中运行,保持一致的运行环境。这样,开发、测试和部署都更为简便。
CPU 分配区别:
物理机 CPU 分配: 在物理机上,CPU 的分配通常是直接针对物理核心的。应用可以充分利用物理机的多个核心,但在高负载时,可能会有资源争用的情况。
容器 CPU 分配: 在容器中,CPU 的分配是基于容器级别的。容器可以限制和分配 CPU 的使用,可以指定每个容器可以使用的 CPU 资源。这使得容器可以更加灵活地进行资源管理,但也需要注意容器之间的资源隔离。
物理机 CPU 分配: 在物理机上,CPU 的分配通常是直接针对物理核心的。应用可以充分利用物理机的多个核心,但在高负载时,可能会有资源争用的情况。
容器 CPU 分配: 在容器中,CPU 的分配是基于容器级别的。容器可以限制和分配 CPU 的使用,可以指定每个容器可以使用的 CPU 资源。这使得容器可以更加灵活地进行资源管理,但也需要注意容器之间的资源隔离。
分配给进程的资源有哪些
CPU时间
内存空间
语法糖有了解么
自动装箱与拆箱(Autoboxing and Unboxing)
增强的for循环(Enhanced for Loop)
泛型(Generics)
可变参数(Varargs)
自动关闭资源(try-with-resources)
lambda
String
String底层数据结构
char[]->1.9byte[]
节省空间
不可变的实现
1. String类被声明为final,这意味着它不能被继承。那么他里面的方法就是没办法被覆盖的。
2. 用final修饰字符串内容的char[](从JDK 1.9开始,char[]变成了byte[]),由于该数组被声明为final,一旦数组被初始化,就不能再指向其他数组。
3. String类没有提供用于修改字符串内容的公共方法。例如,没有提供用于追加、删除或修改字符的方法。如果需要对字符串进行修改,会创建一个新的String对象。
如果String不是不可变量会怎样
缓存
字符串是使用最广泛的数据结构。大量的字符串的创建是非常耗费资源的,所以,Java提供了对字符串的缓存功能——字符串池
通过字符串池,两个内容相同的字符串变量,可以从池中指向同一个字符串对象,从而节省了关键的内存资源。如果字符串是可变的,我们一旦修改了其中一个字符串的的内容,那必然导致另外一个字符串的内容也被动的改变了
通过字符串池,两个内容相同的字符串变量,可以从池中指向同一个字符串对象,从而节省了关键的内存资源。如果字符串是可变的,我们一旦修改了其中一个字符串的的内容,那必然导致另外一个字符串的内容也被动的改变了
安全性
字符串在Java应用程序中广泛用于存储敏感信息,如用户名、密码、连接url、网络连接等。JVM类加载器在加载类的时也广泛地使用它。
如果是可变的,那么这个字符串内容就可能随时都被修改。那么这个字符串内容就完全不可信了。这样整个系统就没有安全性可言了。
如果是可变的,那么这个字符串内容就可能随时都被修改。那么这个字符串内容就完全不可信了。这样整个系统就没有安全性可言了。
线程安全
不可变会自动使字符串成为线程安全的,因为当从多个线程访问它们时,它们不会被更改。
因此,一般来说,不可变对象可以在同时运行的多个线程之间共享。它们也是线程安全的,因为如果线程更改了值,那么将在字符串池中创建一个新的字符串,而不是修改相同的值。因此,字符串对于多线程来说是安全的。
因此,一般来说,不可变对象可以在同时运行的多个线程之间共享。它们也是线程安全的,因为如果线程更改了值,那么将在字符串池中创建一个新的字符串,而不是修改相同的值。因此,字符串对于多线程来说是安全的。
hashcode缓存
由于字符串对象被广泛地用作数据结构,它们也被广泛地用于哈希实现,如HashMap、HashTable、HashSet等。在对这些散列实现进行操作时,经常调用hashCode()方法。
不可变性保证了字符串的值不会改变。因此,hashCode()方法在String类中被重写,以方便缓存,这样在第一次hashCode()调用期间计算和缓存散列,并从那时起返回相同的值
不可变性保证了字符串的值不会改变。因此,hashCode()方法在String类中被重写,以方便缓存,这样在第一次hashCode()调用期间计算和缓存散列,并从那时起返回相同的值
性能
前面提到了的字符串池、hashcode缓存等,都是提升性能的体现。
因为字符串不可变,所以可以用字符串池缓存,可以大大节省堆内存。而且还可以提前对hashcode进行缓存,更加高效
由于字符串是应用最广泛的数据结构,提高字符串的性能对提高整个应用程序的总体性能有相当大的影响。
因为字符串不可变,所以可以用字符串池缓存,可以大大节省堆内存。而且还可以提前对hashcode进行缓存,更加高效
由于字符串是应用最广泛的数据结构,提高字符串的性能对提高整个应用程序的总体性能有相当大的影响。
String s = new String("java")创建了几个对象
如果是第一次执行,那么就是会同时创建两个对象。一个字符串常量引用指向的对象,一个我们new出来的对象。
String s = "a" + "b" + "c" 创建了几个对象
一个
String,Stringbuilder,StringBuffer的区别
String是不可变的,StringBuilder和StringBuffer是可变的。而StringBuffer是线程安全的,而StringBuilder是非线程安全的。
泛型
介绍一下泛型机制
参数化类型(Parameterized Type)来实现,声明的类型参数在使用时可以用具体的类型来替换,主要是在集合类中
泛型的使用方式有哪几种
泛型类、泛型接口、泛型方法
项目中怎么用的
自定义接口通用返回结果 CommonResult<T> 通过参数 T 可根据具体的返回类型动态指定结果的数据类型
泛型擦除是什么?
就是通过语法糖的形式,在.java->.class转换的阶段,将List<String>擦除调转为List的手段。换句话说,Java的泛型只在编译期,Jvm是不会感知到泛型的。
解释一下泛型的作用
1. 方便:可以提高代码的复用性。以List接口为例,我们可以将String、Integer等类型放入List中,如不用泛型,存放String类型要写一个List接口,存放Integer要写另外一个List接口,泛型可以很好的解决这个问题
2. 安全:在泛型出之前,通过Object实现的类型转换需要在运行时检查,如果类型转换出错,程序直接GG,可能会带来毁灭性打击。而泛型的作用就是在编译时做类型检查,这无疑增加程序的安全性
2. 安全:在泛型出之前,通过Object实现的类型转换需要在运行时检查,如果类型转换出错,程序直接GG,可能会带来毁灭性打击。而泛型的作用就是在编译时做类型检查,这无疑增加程序的安全性
泛型的桥方法
桥接方法是在父类、子类的继承场景中出现的。父类是泛型类,且在该类中存在泛型方法。子类继承父类,并实现泛型方法。如果在子类实现中不包含父类经过类型擦除后生成的原始类型方法,则编译器会自动将该原始类型方法添加到子类中。这个被添加的原始类型方法我们称之为桥接方法。
原因
父接口SuperClass定义了test方法,SuperClass经过类型擦除转换为原始类型后,会生成 public void test(java.lang.Object); 方法,我们在SuperClass的子类定义了泛型的具体类型,导致子类中的 test方法变化为 public void test(String s);,
那么此时父类的 public void test(java.lang.Object); 是没有被实现的,为了解决这个问题,Java 编译器通过桥接的方式实现了 public void test(java.lang.Object); 方法。
这样就保证了SuperClass与子类具有相同的一致的方法 public void test(java.lang.Object); 。在访问泛型对象时,通过父类方法 test进行统一调用,而不需要关注子类的具体实现。
那么此时父类的 public void test(java.lang.Object); 是没有被实现的,为了解决这个问题,Java 编译器通过桥接的方式实现了 public void test(java.lang.Object); 方法。
这样就保证了SuperClass与子类具有相同的一致的方法 public void test(java.lang.Object); 。在访问泛型对象时,通过父类方法 test进行统一调用,而不需要关注子类的具体实现。
为什么要有包装类
因为Java是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将int 、double等类型放进去的。因为集合的容器要求元素是Object类型。
为了让基本类型也具有对象的特征,就出现了包装类型,它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
为了让基本类型也具有对象的特征,就出现了包装类型,它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
接口和抽象类的区别,使用场景
方法定义:接口和抽象类,最明显的区别就是接口只是定义了一些方法而已,接口中只有抽象方法,是没有实现的代码的。(Java8中可以有默认方法)
修饰符:抽象类中的抽象方法可以有public、protected和private和<defaiult>这些修饰符,而接口中默认修饰符是public。不可以使用其它修饰符。(接口中,如果定义了成员变量,还必须要初始化)
构造器:抽象类可以有构造器。接口不能有构造器。
单继承,多实现:一个类可以实现多个接口,但只能继承一个抽象类。接口支持多重继承,即一个接口可以继承多个其他接口。
所以当我们想要定义标准、规范的时候,就使用接口。当我们想要复用代码的时候,就使用抽象类。
1.8的新特性有什么
default接口默认方法。允许接口中添加非抽象的方法实现,使用default关键字,这使得我们可以向现有的接口中添加新的方法,而不会破坏已有的实现类
Lambda表达式和函数式接口。Lambda表达式本质上是一段匿名内部类,可以将函数作为方法的参数进行传递。使用Lambda表达式可以让代码更加简洁,但需要注意不要滥用,以免影响代码的可读性。
Stream API。Stream API是处理集合类的关键抽象概念,它允许使用函数式编程风格对集合进行复杂的操作,结合Lambda表达式可以轻松地对集合进行过滤、映射、查找等操作。
日期时间API。改进了对日期和时间的处理方式,新的API提供了更加直观和灵活的方法来处理日期时间和时间间隔。
Optional类。在Java 8之前,Google Guava项目引入了Optional作为一种避免空指针异常的方式。Optional类现在已经成为Java 8库的一部分,它鼓励程序员编写更加整洁和健壮的代码,避免代码中过多的null检查。
多核处理器支持。JDK 1.8对多核处理器有更好的处理,提供了更简单的新思路来支持并行操作,避免了用synchronized编写代码,这一代码不仅容易出错,而且在多核CPU上执行所需的成本也比你想象的要高。
现在JDK的最新版本是什么
21
JDK 8中推出了Lambda表达式、Stream、Optional、新的日期API等
JDK 9中推出了模块化
JDK 10中推出了本地变量类型推断
JDK 12中增加了switch表达式
JDK 13中增加了text block
JDK 14中增加了Records
JDK 14中增加了instance模式匹配
JDK 15中增加了封闭类
JDK 17中扩展了switch模式匹配
JDK 19中增加了协程
JDK 9中推出了模块化
JDK 10中推出了本地变量类型推断
JDK 12中增加了switch表达式
JDK 13中增加了text block
JDK 14中增加了Records
JDK 14中增加了instance模式匹配
JDK 15中增加了封闭类
JDK 17中扩展了switch模式匹配
JDK 19中增加了协程
序列化和反序列化怎么理解
如果我们需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些场景都需要用到序列化。简单来说:序列化:将数据结构或对象转换成二进制字节流的过程反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程
lombok的使用方式和优缺点
优点
代码简洁
缺点
团队强制使用
需要了解底层原理
当我们使用@Data定义一个类的时候,会自动帮我们生成equals()方法 。
但是如果只使用了@Data,而不使用@EqualsAndHashCode(callSuper=true)的话,会默认是
@EqualsAndHashCode(callSuper=false),这时候生成的equals()方法只会比较子类的属性,不会考虑从父类继承的属性,无论父类属性访问权限是否开放。
但是如果只使用了@Data,而不使用@EqualsAndHashCode(callSuper=true)的话,会默认是
@EqualsAndHashCode(callSuper=false),这时候生成的equals()方法只会比较子类的属性,不会考虑从父类继承的属性,无论父类属性访问权限是否开放。
影响升级
如果我们需要升级到某个新版本的JDK的时候,若其中的特性在Lombok中不支持的话就会受到影响。
还有一个可能带来的问题,就是Lombok自身的升级也会受到限制。
还有一个可能带来的问题,就是Lombok自身的升级也会受到限制。
破坏封装性
如果我们在代码中直接使用Lombok,那么他会自动帮我们生成getter、setter 等方法,这就意味着,一个类中的所有参数都自动提供了设置和读取方法。
集合
hashmap
1.7
数据结构
数组+链表
扰动函数变化
9次 4次位运算+5次异或
头插法
线程安全问题 环形链表
元素求桶的位置的方法
每一个键值对都要重新计算(新的数组的长度-1)
1.8
数据结构
数组+链表+红黑树
数组容量大于64且链表长度大于8的情况下会使用红黑树
红黑树节点数<=6的时候退化成链表
扰动函数变化
1次位运算+1次异或
尾插法
在并发执行put操作时会发生数据覆盖的情况
元素求桶的位置的方法
直接通过hash(k) & oldCap(与旧数组的长度进行与操作)进行分类,之后统一移动他们的位置即可
如果是0,那么就代表当前元素的桶位置不变
并发问题
1. 多线程put的时候,size的个数和真正的个数不一样
2. 多线程put的时候,可能会把上一个put的值覆盖掉
3. 和其他不支持并发的集合一样,HashMap也采用了fail-fast操作,当多个线程同时put和get的时候,会抛出并发异常
4. 当既有get操作,又有扩容操作的时候,有可能数据刚好被扩容换了桶,导致get不到数据
2. 多线程put的时候,可能会把上一个put的值覆盖掉
3. 和其他不支持并发的集合一样,HashMap也采用了fail-fast操作,当多个线程同时put和get的时候,会抛出并发异常
4. 当既有get操作,又有扩容操作的时候,有可能数据刚好被扩容换了桶,导致get不到数据
默认负载因子0.75
容量2^n次方
怎么降低hashmap冲突
负载因子减小
增大容量
hashmap 可以不用链表 红黑树么
CurrentHashMap
JDK1.7版本的ReentrantLock+Segment+HashEntry
JDK1.8版本中synchronized+CAS+HashEntry+红黑树
ArrayList和 LinkedList 的区别?扩容方式?
ArrayList是可改变大小的数组
申请容量1.5倍的数组,复制
LinkedList双向链表
使用HashMap如果冲突比较高链表的效率岂不是很差? HashMap有做过这方面的优化么
hashmap的数据结构 里面的红黑树删除元素了之后会不会退化为链表
8个转红黑树
6个退回链表
MySQL
B+树每个结点存几条数据
一个节点可以存储一页数据16k
InnoDB B+树深度如何计算
user(sex,age,city,name) 查询出北京年龄大于35的男用户,什么字段加索引
CREATE INDEX idx_city_age_sex ON user (city, age, sex)
update数据的时候会影响select吗
锁(行锁或表锁)的影响,导致select 阻塞
事务隔离级别不同,读取的数据不同
查询计划的改变,索引的影响
如果一条SQL查询速度特别慢应该如何排查
确认问题
explain分析查询
检查索引
检查查询条件
检查硬件和资源
优化SQL
监控和日志
数据库优化
分析工具
考虑缓存
如果SQL查询特别慢是因为表数据太大引起的应该如何解决
优化查询
添加索引
使用缓存
升级硬件资源
数据库分片
分区表
数据归档和清理
使用分布式数据库
数据库优化
Mysql索引设置注意点?
选择适当的索引列
避免创建过多的索引
考虑复合索引
前缀索引
MySQL的InnoDB引擎 用自增值和非自增作值作主键有什么区别
id
存储空间小,查询效率高,方便展示,分页方便
分库分表注意重复,能够被预测缺乏安全性,范围有限可能被用尽
uuid
全局唯一,不可预测,可应用分布式中
存储空间大,范围查询难,不方便展示,查询效率低
select * from t where a >10:有a索a_b联合索引,会走哪个索引,原因
通常会选择使用具有更高选择性的单列索引 a
聚集索引和非聚集索引的区别,查询的底层逻辑有什么区别,有什么优化的方案
MySQL同时支持多个行锁么
支持
主键索引和唯一索引和联合索引区别,分别对数据库的作用?
唯一性:主键索引其实是一种特殊的唯一索引,他们都具有唯一性;也就是在一张表中,不能有两行有相同的主键;在同一张表中,主键索引字段的所有值都是唯一的
是否可空:主键索引是不能为NULL的,而唯一键索引是可以为NULL的。
是否可以有多个:主键索引在一张表中只能有一个,而唯一索引在一张表中可以创建多个。
索引结构:在InnoDB中,主键索引就是聚簇索引,而唯一索引通常是非聚簇索引。
是否回表:基于主键索引的查询一定不需要回表,基于唯一索引的查询,通常是需要回表的(这里的通常和上面的通常是一个事儿)
外键:主键可以被其他表引用为外键,而唯一索引是不可以的。
联合索引的顺序是ABC,然后where CBA,他会走索引吗?
虽然查询条件的顺序与索引的顺序不完全一致,但MySQL通常会重新排列查询条件以充分利用索引,这被称为"索引下推优化"
怎么实现给mysql授权给一个用户?
GRANT permission_type ON database_name.table_name TO 'username'@'host';
手撕mysql建表
CREATE TABLE table_name (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
age INT,
gender VARCHAR(10),
city VARCHAR(255),
UNIQUE KEY (id),
INDEX city_age_gender (city, age, gender)
) ENGINE=InnoDB;
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
age INT,
gender VARCHAR(10),
city VARCHAR(255),
UNIQUE KEY (id),
INDEX city_age_gender (city, age, gender)
) ENGINE=InnoDB;
MySQL给你个SQL如何调优,有什么思路?
分析执行计划
索引优化
查询优化
适当的数据类型
分页查询
索引优化详细讲讲 (怎么用,怎么设置)
JDBC接口,流程了解么,常用的方法有什么,在哪个包下
工作流程
加载数据库驱动:JDBC连接的第一步是加载需要连接的数据库的JDBC驱动。
建立连接:使用DriverManager类的getConnection方法,提供数据库URL、用户名和密码来建立到数据库的连接。
创建Statement/PreparedStatement/CallableStatement对象:通过连接对象创建一个或多个SQL语句的对象。
执行SQL语句:使用Statement对象执行SQL语句。可以是查询(SELECT),也可以是更新(INSERT、UPDATE、DELETE)。
处理结果:如果执行的是查询操作,会返回一个ResultSet对象,通过它可以读取查询结果。
关闭连接:操作完成后,需要依次关闭ResultSet、Statement和连接对象。
建立连接:使用DriverManager类的getConnection方法,提供数据库URL、用户名和密码来建立到数据库的连接。
创建Statement/PreparedStatement/CallableStatement对象:通过连接对象创建一个或多个SQL语句的对象。
执行SQL语句:使用Statement对象执行SQL语句。可以是查询(SELECT),也可以是更新(INSERT、UPDATE、DELETE)。
处理结果:如果执行的是查询操作,会返回一个ResultSet对象,通过它可以读取查询结果。
关闭连接:操作完成后,需要依次关闭ResultSet、Statement和连接对象。
常用方法
加载驱动:
Class.forName(String className):用于加载数据库驱动。
建立连接:
DriverManager.getConnection(String url, String user, String password):获取数据库连接。
执行SQL语句:
Connection.createStatement():创建Statement对象,用于执行静态SQL语句。
Connection.prepareStatement(String sql):创建PreparedStatement对象,用于执行预编译SQL语句。可以提高性能并防止SQL注入。
Connection.prepareCall(String sql):创建CallableStatement对象,用于执行数据库存储过程。
处理查询结果:
ResultSet.next():移动ResultSet的光标到下一行,如果有数据返回true,否则返回false。
ResultSet.getInt(String columnLabel)、ResultSet.getString(String columnLabel)等:根据列名获取数据。
事务管理:
Connection.setAutoCommit(boolean autoCommit):设置事务是否自动提交。
Connection.commit():手动提交事务。
Connection.rollback():回滚事务。
Class.forName(String className):用于加载数据库驱动。
建立连接:
DriverManager.getConnection(String url, String user, String password):获取数据库连接。
执行SQL语句:
Connection.createStatement():创建Statement对象,用于执行静态SQL语句。
Connection.prepareStatement(String sql):创建PreparedStatement对象,用于执行预编译SQL语句。可以提高性能并防止SQL注入。
Connection.prepareCall(String sql):创建CallableStatement对象,用于执行数据库存储过程。
处理查询结果:
ResultSet.next():移动ResultSet的光标到下一行,如果有数据返回true,否则返回false。
ResultSet.getInt(String columnLabel)、ResultSet.getString(String columnLabel)等:根据列名获取数据。
事务管理:
Connection.setAutoCommit(boolean autoCommit):设置事务是否自动提交。
Connection.commit():手动提交事务。
Connection.rollback():回滚事务。
MySQL
事务
特性
原子性
一致性
隔离性
持久性
一致性
隔离性
持久性
事务并发存在的问题
脏读(dirty read)
事务A读取到事务B未提交的数据
不可重复读(unrepeatable read)
在事务A范围内,由于事务B的更新操作,两个相同的查询,读取同一条记录,却返回了不同的数据,这就是不可重复读
幻读
事务A查询一个范围的结果集,另一个并发事务B往这个范围中插入/删除了数据,并提交
然后事务A再次查询相同的范围,两次读取得到的结果集不一样
然后事务A再次查询相同的范围,两次读取得到的结果集不一样
隔离级别
读未提交(Read Uncommitted)
一个事务会读到其他事务未提交的数据的,即存在脏读问题
读未提交,采取的是读不加锁原理。
事务读不加锁,不阻塞其他事务的读和写
事务写阻塞其他事务写,但不阻塞其他事务读;
事务读不加锁,不阻塞其他事务的读和写
事务写阻塞其他事务写,但不阻塞其他事务读;
读已提交(Read Committed)
当前事务只能读取到其他事务提交的数据。解决了脏读
可重复读(Repeatable Read)
当前事务不能读取到其他事务提交的数据。解决了不可重复读
没有解决幻读
Binsert Aupdate Aselect 多了一行
串行化(Serializable)
所有SELECT语句会隐式转化为 SELECT...FOR SHARE,即加共享锁。
读加共享锁,写加排他锁,读写互斥。如果有未提交的事务正在修改某些行,所有select这些行的语句都会阻塞。
读加共享锁,写加排他锁,读写互斥。如果有未提交的事务正在修改某些行,所有select这些行的语句都会阻塞。
Mysql 默认的事务隔离级别是可重复读(Repeatable Read)
实现原理
Innodb在RR隔离级别下怎么解决幻读
MySql使用不同的锁策略(Locking Strategy)/MVCC来实现四种不同的隔离级别。
RR、RC的实现原理跟MVCC有关,RU和Serializable跟锁有关。
RR、RC的实现原理跟MVCC有关,RU和Serializable跟锁有关。
RR跟RC隔离级别,最大的区别就是:
RC每次读取数据前都生成一个ReadView,
而RR只在第一次读取数据时生成一个ReadView。
RC每次读取数据前都生成一个ReadView,
而RR只在第一次读取数据时生成一个ReadView。
Innodb在RR隔离级别下怎么解决幻读
针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好了避免幻读问题。
针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select ... for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。
MVCC的实现原理
多版本并发控制,它是通过读取历史版本的数据,来降低并发事务冲突,从而提高并发性能的一种机制。
它的实现依赖于隐式字段、undo日志、快照读&当前读、Read View
它的实现依赖于隐式字段、undo日志、快照读&当前读、Read View
隐式字段
InnoDB存储引擎,每一行记录都有两个隐藏列DBTRXID,DBROLLPTR,
如果表中没有主键和非NULL唯一键时,则还会有第三个隐藏的主键列 DBROWID。
如果表中没有主键和非NULL唯一键时,则还会有第三个隐藏的主键列 DBROWID。
DBTRXID,记录每一行最近一次修改(修改/更新)它的事务ID,大小为6字节;
DBROLLPTR,这个隐藏列就相当于一个指针,指向回滚段的undo日志,大小为7字节;
DBROWID,单调递增的行ID,大小为6字节
DBROLLPTR,这个隐藏列就相当于一个指针,指向回滚段的undo日志,大小为7字节;
DBROWID,单调递增的行ID,大小为6字节
undo日志
事务未提交的时候,修改数据的镜像(修改前的旧版本),存到undo日志里。
以便事务回滚时,恢复旧版本数据,撤销未提交事务数据对数据库的影响。
undo日志是逻辑日志。可以这样认为,当delete一条记录时,undo log中会记录一条对应的insert记录,当update一条记录时,它记录一条对应相反的update记录。
存储undo日志的地方,就是回滚段。
以便事务回滚时,恢复旧版本数据,撤销未提交事务数据对数据库的影响。
undo日志是逻辑日志。可以这样认为,当delete一条记录时,undo log中会记录一条对应的insert记录,当update一条记录时,它记录一条对应相反的update记录。
存储undo日志的地方,就是回滚段。
多个事务并行操作某一行数据时,不同事务对该行数据的修改会产生多个版本,然后通过回滚指针(DBROLLPTR)连一条Undo日志链。
快照读&当前读
快照读:
读取的是记录数据的可见版本(有旧的版本),不加锁,普通的select语句都是快照读
读取的是记录数据的可见版本(有旧的版本),不加锁,普通的select语句都是快照读
select * from account where id>2;
当前读:
读取的是记录数据的最新版本,显示加锁的都是当前读
读取的是记录数据的最新版本,显示加锁的都是当前读
select * from account where id>2 lock in share mode
select * from account where id > 2 for update
select * from account where id > 2 for update
Read View
Read View就是事务执行快照读时,产生的读视图。
事务执行快照读时,会生成数据库系统当前的一个快照,记录当前系统中还有哪些活跃的读写事务,把它们放到一个列表里。
Read View主要是用来做可见性判断的,即判断当前事务可见哪个版本的数据
事务执行快照读时,会生成数据库系统当前的一个快照,记录当前系统中还有哪些活跃的读写事务,把它们放到一个列表里。
Read View主要是用来做可见性判断的,即判断当前事务可见哪个版本的数据
版本何时生成的,有多少个版本
索引
分类
数据结构分类
B+tree索引
Full-text索引
物理存储分类
聚簇索引
叶子节点存储了一行完整的表数据,叶子节点间按id列递增连接
非聚簇索引
二级索引
二级索引
叶子节点并不存储一行完整的表数据,而是存储了聚簇索引所在列的值。
字段特性分类
主键索引
建立在主键上的索引被称为主键索引,一张数据表只能有一个主键索引,索引列值不允许有空值,通常在创建表时一起创建。
缺点
更新数据需要维护索引
唯一索引
建立在UNIQUE字段上的索引被称为唯一索引,一张表可以有多个唯一索引,索引列值允许为空,列值中出现多个空值不会发生重复冲突。
普通索引
建立在普通字段上的索引被称为普通索引。
前缀索引
对字符类型字段的前几个字符或对二进制类型字段的前几个bytes建立的索引,而不是在整个字段上建索引
字段个数分类
单列索引
建立在单个列上的索引被称为单列索引。
联合索引
建立在多个列上的索引被称为联合索引,又叫复合索引、组合索引。
常见题
覆盖索引
查询列要被所建的索引覆盖,不必从数据表中读取,换句话说查询列
要被所使用的索引覆盖。
要被所使用的索引覆盖。
回表
二级索引无法直接查询所有列的数据,所以通过二级索引查询到聚簇索引后,
再查询到想要的数据,这种通过二级索引查询出来的过程,就叫做回表。
再查询到想要的数据,这种通过二级索引查询出来的过程,就叫做回表。
索引失效
查询条件包含or,可能导致索引失效
如果字段类型是字符串,where时一定用引号括起来,否则索引失效
like通配符可能导致索引失效
like查询是以%开头,才会导致索引失效
联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。
最左前缀原则
锁
为什么需要锁
锁的使用可以确保每个操作都按预期执行,从而保证数据的完整性和一致性
乐观锁
它假设事务之间的冲突很少发生,因此不主动对数据进行加锁。乐观领的实现方式通常使用数据版本号或时间戳
在MYSQL中,常见的乐观锁实现方式是使用版本号
乐观锁的优势在于减少锁竞争,提亮并发性能,但也增加了冲突检测和处理的复杂性
悲观锁
它假设事务之间的冲突经常发生,因此采取主动加锁来保证事务的安全性
行锁
行锁是针对数据表中的行纪录进行加锁的机制。它可以实现更细粒度的并发控制
开销大,加锁慢,会出现死锁,优点是锁定粒度最小,发生锁冲突的概率最低,并发度也最高
共享锁 (Shared Lock)
允许多个事务同时读取同一行数据,但不允许任何事务对该行数据进行修改操作
在电商项目中,共享领可以用于商品库存的读取操作,骑保多个用户同时谈取库存数据而不会产生冲突
排它锁(xcluslve Lock)
一个事务持有捧它锁时,其他事务无法读取和修改行数据
在电商项目中,排他锁可以用于商品库存的更斯摇作,确保只有一个用户时以修改库存数据,避免并发写入导致毁据不一数的问题
记录锁(Record Lock)
记录锁只允许一个事务持有,其他事务无法读取和修改该记录
在电商项目中,记录锁可以用于订单表的加锁操作。保证每个订单只能被一个事务处理
间隙锁(gap lock)
防止其他事务在锁定范围内插入新的记录
在电商项目中,间隙锁可以用于商品库存的范围查询操作,防止其他事务在查询过程中插入新的库存记录
临键锁
防止其他事务在范围内插入新的记录或修改现有记录
在电商项目中,临建锁可以用于商品的售卖操作。保证在购买商品时其他事务无法修改商品信息
表锁
表锁整对整个数据表进行加锁的机制,在表锁模式下。锁的粒度比行锁大,控制并发的能力相对较弱
开销小,加锁快;不会出现死锁,缺点是锁定粒度大,发出锁冲突的概率最高,并发度最低。
意向锁
当一个事务持有行锁时,他必须首先获取意向锁
在电商项目中,意向锁可以用于商品订单表的加锁操作,表示事务将对该表进行行级锁定
自增锁
在自增锁模式下,只有一个事务可以递增自增字段
在电商项目中,自增锁可以用于订单号的生成操作,确保每个事务生成的订单号不会重复
锁模式
IX意向排它锁
它表示一个事务对某个表的某个行记录或范围进行排它锁操作
X表示排它锁
它表示一个事务对某个表的某个行记录进行排它锁操作
S表示共享锁
它表示一个事务对某个表的某个行记录进行共享锁操作
其他
执行流程
SELECT
建立连接-查缓存-解析器-预处理器-优化器-执行计划-执行器-存储引擎
UPDATE
修改结果刷到缓存-redolog prepare - 提交事务-写binlog-commit事务-redolog commit
咕泡的图
崩溃恢复
binlog无记录 redolog无记录
在redolog写之前crash,恢复操作: 回滚事务
binlog无记录,redolog状态prepare
在binlog写完之前的crash,恢复操作: 回滚事务
binlog有记录,redolog状态prepare
在binlog写完提交事务之前的crash,恢复操作:提交事务
binlog有记录,redolog状态commit
正常完成的事务,不需要恢复
LOG
binlog
undolog
redolog
eic
expain
MySQL中既存在redo log,又存在bin log,这是因为bin log是MySQL Server提供的一种归档日志,其本身并不具备crash-safe能力。而redo log本身不具备归档能力,他是一种循环写的日志
JVM
可达性分析
虚拟机栈(栈帧中的本地变量表)中引用的对象。(方法的局部变量)
方法区中类静态属性引用的对象。(类的静态变量,使用static关键字)
方法区中常量引用的对象。(使用static final关键字修饰的常量)
本地方法栈中JNI(即一般说的Native方法)引用的对象。
java为什么会有并发问题?(java内存模型,栈区独享,堆区共享)
Java中的并发问题主要源于多线程的运行方式及其对共享资源的访问和修改。在多线程环境下,当多个线程同时执行并尝试读取和修改同一资源(如变量、数据结构、文件等),如果没有适当的协调和同步机制,就可能导致数据不一致、数据丢失或者状态错误等问题
对象是根据什么策略回收的?(堆区结构,可达性分析算法,垃圾回收算法)
标记清除,标记复制,标记整理
怎么指定垃圾回收器
-XX:+UseG1GC
–XX:+UseConcMarkSweepGC
对象在堆的一生
创建阶段(Created)
应用阶段(Using)
不可视阶段(Invisible)
不可达阶段(Unreachable)
可收集阶段(Collected)
终结阶段(Finalized)
对象空间重新分配(Deallocated)
频繁fullgc怎么排查 解决
监控和记录GC日志
分析GC日志
分析内存快照
调整堆内存大小
查看GC原因
检查对象的生命周期
更换垃圾回收器
线上cpu100%怎么排查
监控工具
top:用于查看进程的CPU使用率。
查找高CPU进程(PID)
分析高CPU进程
使用ps命令查看进程的详细信息
使用jstack分析Java进程的线程堆栈,以查看线程的状态和活动
查看日志
性能剖析
代码审查
数据库查询
资源竞争
系统资源
ivm虚拟机组成部分,各版本虚拟机差异
如何查看ivm虚拟机中堆区内存状况
jvisualvm
jstat
jconsole
VisualVM
你刚提到了outofmemory,jvm什么情况会发生outofmemory
堆内存已经耗尽,无法为新的对象分配足够的内存空间
堆内存不足
内存泄漏
元空间不足
对象的创建过程
类加载顺序
加载(Loading)
链接(Linking)
验证(Verification)
准备(Preparation)
解析(Resolution)
初始化(Initialization)
父类和子类 的加载顺序
总结:先静态后非静态,先父类后子类
-父类静态变量、静态代码块(若有多个按代码先后顺序执行)
-子类静态变量、静态代码块(若有多个按代码先后顺序执行)
-父类非静态变量、非静态代码块(若有多个按代码先后顺序执行)
-父类构造函数
-子类非静态变量、非静态代码块(若有多个按代码先后顺序执行)
-子类构造函数
-父类静态变量、静态代码块(若有多个按代码先后顺序执行)
-子类静态变量、静态代码块(若有多个按代码先后顺序执行)
-父类非静态变量、非静态代码块(若有多个按代码先后顺序执行)
-父类构造函数
-子类非静态变量、非静态代码块(若有多个按代码先后顺序执行)
-子类构造函数
如何排查 Java 进程 CPU 飙高,如何排查
CPU使用率
线程数
垃圾回收
线程状态
方法调用栈
外部资源调用
new一个对象的过程,讲的详细一些
类加载
内存分配
初始化
构造函数调用
返回对象引用
垃圾回收
CMS和G1的区别
CMS和G1的流程
CMS
初始标记(Initial Mark):
开始GC时,暂停所有应用线程,进行一次短暂的停顿,标记GC根对象,并标记所有直接与GC根对象有关的对象。
并发标记(Concurrent Mark):
恢复应用线程,同时GC线程对堆进行并发标记,标记所有从GC根对象可达的对象,直到标记完成。
重新标记(Remark):
暂停应用线程,进行一次短暂的停顿,重新扫描一遍堆,修正并发标记阶段中因应用线程更新对象状态而发生变化的对象,并标记这些对象。
并发清除(Concurrent Sweep):
恢复应用线程,同时GC线程对堆进行并发清除,清除标记为垃圾的对象,释放它们所占用的内存空间。
开始GC时,暂停所有应用线程,进行一次短暂的停顿,标记GC根对象,并标记所有直接与GC根对象有关的对象。
并发标记(Concurrent Mark):
恢复应用线程,同时GC线程对堆进行并发标记,标记所有从GC根对象可达的对象,直到标记完成。
重新标记(Remark):
暂停应用线程,进行一次短暂的停顿,重新扫描一遍堆,修正并发标记阶段中因应用线程更新对象状态而发生变化的对象,并标记这些对象。
并发清除(Concurrent Sweep):
恢复应用线程,同时GC线程对堆进行并发清除,清除标记为垃圾的对象,释放它们所占用的内存空间。
G1
初始标记(Initial Mark):
类似于CMS,G1也有一个初始标记阶段,标记被直接根引用的对象,这个阶段需要短暂停顿。
并发标记(Concurrent Mark):
与CMS类似,G1通过并发标记阶段来标记所有可达对象,与此同时,应用线程继续执行。
最终标记(Final Mark):
为了处理在并发标记期间引用关系的变化,G1进行最终标记阶段,暂停应用线程,标记被直接根引用的对象。
筛选回收(Live Data Counting and Evacuation):
G1在标记阶段的同时估算每个存活对象的回收价值,通过回收价值来优先选择哪些区域进行垃圾回收。然后,G1会选择部分价值较低的区域进行回收,并将存活对象从这些区域移动到空闲区域。
并发清理(Concurrent Cleanup):
与CMS一样,G1的清理阶段也是并发执行的,垃圾回收器清理未被标记的对象。
类似于CMS,G1也有一个初始标记阶段,标记被直接根引用的对象,这个阶段需要短暂停顿。
并发标记(Concurrent Mark):
与CMS类似,G1通过并发标记阶段来标记所有可达对象,与此同时,应用线程继续执行。
最终标记(Final Mark):
为了处理在并发标记期间引用关系的变化,G1进行最终标记阶段,暂停应用线程,标记被直接根引用的对象。
筛选回收(Live Data Counting and Evacuation):
G1在标记阶段的同时估算每个存活对象的回收价值,通过回收价值来优先选择哪些区域进行垃圾回收。然后,G1会选择部分价值较低的区域进行回收,并将存活对象从这些区域移动到空闲区域。
并发清理(Concurrent Cleanup):
与CMS一样,G1的清理阶段也是并发执行的,垃圾回收器清理未被标记的对象。
内存分配机制和场景
指针碰撞:当堆中的内存是连续的,JVM使用一个指针来标记当前可用的内存位置,然后将指针向前移动分配对象所需的内存大小。
空闲列表:当堆中的内存是离散的,JVM会维护一个空闲列表,记录可用的内存块。在分配对象时,JVM会遍历空闲列表,找到足够大小的内存块进行分配。
指针碰撞适用于堆内存连续的情况,它简单高效,但对内存的要求较高。对应着Serial,ParNew GC等垃圾回收器。同时也对应着标记整理算法,复制算法等GC算法
空闲列表适用于堆内存离散的情况,它可以更灵活地利用内存碎片,但需要额外的空间来维护和查找空闲内存块。对应CMS垃圾回收器,算法为标记清除算法
空闲列表适用于堆内存离散的情况,它可以更灵活地利用内存碎片,但需要额外的空间来维护和查找空闲内存块。对应CMS垃圾回收器,算法为标记清除算法
GCroot可达性分析有什么
三色标记法
可达性分析算法
引用计数法
公司用的什么垃圾回收器,为什么
Spring
Controller定义一个变量会有线程安全问题么
会,controller是单例的,数据共享必然会有线程安全问题
为什么service变量不会有并发请求问题
每个请求一个service对象
自己new对象和依赖注入 优缺点
请求参数 对应vo 如何怎么实现的
事务如何实现的
@Transactional为什么能实现事务?
spring三级缓存
一级缓存
完整bean\成熟的bean
二级缓存
代理bean\早期bean
三级缓存
bean工厂
beanfactory factorybean
BeanFactory
BeanFactory是个Factory,也就是IOC容器或对象工厂
定义了IOC容器的最基本形式,并提供了IOC容器应遵守的的最基本的接口,也就是Spring IOC所遵守的最底层和最基本的编程规范
使用BeanFactory创建对象的时候,必须要遵循严格的生命周期流程,太复杂了
FactoryBean
FactoryBean是个Bean
这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似
如果想要简单的自定义某个对象的创建,同时创建完成的对象想交给spring来管理,那么就需要实现FactroyBean接口了
isSingleton:是否是单例对象
getObjectType:获取返回对象的类型
getObject:自定义创建对象的过程(new,反射,动态代理)
getObjectType:获取返回对象的类型
getObject:自定义创建对象的过程(new,反射,动态代理)
@autowired
和reference的区别
循环依赖
一个接口四个不同实现类如何注入
AOP有哪些通知类型,每种类型的应用场景
1、前置通知(Before Advice):
执行时机: 在连接点执行之前调用通知。
应用场景: 适用于执行某个方法之前需要进行一些准备工作,例如权限检查、日志记录等。
2、后置通知(After Returning Advice):
执行时机: 在连接点正常执行完毕后调用通知。
应用场景: 适用于需要在方法成功执行后执行一些操作,例如记录返回值、清理资源等。
3、异常通知(After Throwing Advice):
执行时机: 在连接点抛出异常时调用通知。
应用场景: 适用于需要在方法抛出异常时执行一些操作,例如异常日志记录、发送通知等。
4、最终通知(After (finally) Advice):
执行时机: 在连接点执行完毕(无论成功还是抛出异常)后调用通知。
应用场景: 适用于需要在方法执行完毕后无论如何都执行一些操作,例如资源释放、清理工作等。
5、环绕通知(Around Advice):
执行时机: 在连接点之前和之后都调用通知。它可以完全控制连接点的执行。
应用场景: 适用于需要完全控制连接点执行过程,可以在方法执行前后进行一些自定义的操作,例如性能监控、事务管理等。
执行时机: 在连接点执行之前调用通知。
应用场景: 适用于执行某个方法之前需要进行一些准备工作,例如权限检查、日志记录等。
2、后置通知(After Returning Advice):
执行时机: 在连接点正常执行完毕后调用通知。
应用场景: 适用于需要在方法成功执行后执行一些操作,例如记录返回值、清理资源等。
3、异常通知(After Throwing Advice):
执行时机: 在连接点抛出异常时调用通知。
应用场景: 适用于需要在方法抛出异常时执行一些操作,例如异常日志记录、发送通知等。
4、最终通知(After (finally) Advice):
执行时机: 在连接点执行完毕(无论成功还是抛出异常)后调用通知。
应用场景: 适用于需要在方法执行完毕后无论如何都执行一些操作,例如资源释放、清理工作等。
5、环绕通知(Around Advice):
执行时机: 在连接点之前和之后都调用通知。它可以完全控制连接点的执行。
应用场景: 适用于需要完全控制连接点执行过程,可以在方法执行前后进行一些自定义的操作,例如性能监控、事务管理等。
Bean有哪些注入方式
Bean生命周期 (如何在创建完bean后打印语句?)
MVC流程
Spring 常用的注解?两种注入方式的区别?
SpringBootApplication 这个注解的解释?
@SpringBootApplication 是一个用于标识Spring Boot应用程序主类的注解。它整合了以下三个注解:
@Configuration:标识该类是一个配置类,通常用于定义配置信息或将其他 Bean 定义在该类中。
@EnableAutoConfiguration:开启自动配置功能,Spring Boot会根据项目的依赖自动配置应用程序所需的配置,大大减少了开发者的配置工作。
@ComponentScan:启用组件扫描,使Spring Boot能够扫描并识别应用程序中的组件(包括 @Controller、@Service、@Repository 等)。
通过这三个注解的组合,@SpringBootApplication 可以让Spring Boot应用程序变得非常简洁,同时提供了许多便捷的功能。
@SpringBootApplication 是一个用于标识Spring Boot应用程序主类的注解。它整合了以下三个注解:
@Configuration:标识该类是一个配置类,通常用于定义配置信息或将其他 Bean 定义在该类中。
@EnableAutoConfiguration:开启自动配置功能,Spring Boot会根据项目的依赖自动配置应用程序所需的配置,大大减少了开发者的配置工作。
@ComponentScan:启用组件扫描,使Spring Boot能够扫描并识别应用程序中的组件(包括 @Controller、@Service、@Repository 等)。
通过这三个注解的组合,@SpringBootApplication 可以让Spring Boot应用程序变得非常简洁,同时提供了许多便捷的功能。
Tomcat是如何启动Spring工程的?
部署Spring工程: 将打包好的Spring工程(通常是一个WAR文件)放置到Tomcat的webapps目录下。
1、Tomcat启动: 启动Tomcat服务器。
2、Tomcat加载WAR文件: Tomcat在启动过程中会扫描webapps目录,检测到WAR文件后会将其解压并加载到内存中。
3、初始化Servlet容器: Tomcat在加载WAR文件后会初始化Servlet容器,并根据web.xml或者Servlet注解配置加载相应的Servlet和Filter。
4、加载Spring的DispatcherServlet: Spring应用通常会配置一个DispatcherServlet来接收所有的HTTP请求,并将它们分发到合适的处理器。在Spring工程中,会有一个类似于web.xml的配置文件(通常是一个Java配置类或者XML文件),其中会配置DispatcherServlet。
5、DispatcherServlet初始化: 当Tomcat启动时,它会加载Spring的DispatcherServlet,并调用它的初始化方法。在初始化过程中,DispatcherServlet会加载Spring应用程序上下文(ApplicationContext),并进行一系列的初始化操作,包括扫描组件、加载Bean定义等。
6、Spring应用程序上下文初始化: DispatcherServlet加载Spring应用程序上下文后,Spring会根据配置文件或者注解来初始化所有的Bean,并建立Bean之间的依赖关系。
7、处理请求: 当Tomcat接收到HTTP请求时,DispatcherServlet会根据请求的URL将请求分发给合适的Controller处理。Controller会处理请求,并调用相应的Service或者DAO层来完成业务逻辑。
总的来说,Tomcat作为Servlet容器,通过加载Spring提供的DispatcherServlet来启动和管理Spring工程。Spring的DispatcherServlet负责接收HTTP请求并分发到相应的处理器,从而启动Spring应用程序。
1、Tomcat启动: 启动Tomcat服务器。
2、Tomcat加载WAR文件: Tomcat在启动过程中会扫描webapps目录,检测到WAR文件后会将其解压并加载到内存中。
3、初始化Servlet容器: Tomcat在加载WAR文件后会初始化Servlet容器,并根据web.xml或者Servlet注解配置加载相应的Servlet和Filter。
4、加载Spring的DispatcherServlet: Spring应用通常会配置一个DispatcherServlet来接收所有的HTTP请求,并将它们分发到合适的处理器。在Spring工程中,会有一个类似于web.xml的配置文件(通常是一个Java配置类或者XML文件),其中会配置DispatcherServlet。
5、DispatcherServlet初始化: 当Tomcat启动时,它会加载Spring的DispatcherServlet,并调用它的初始化方法。在初始化过程中,DispatcherServlet会加载Spring应用程序上下文(ApplicationContext),并进行一系列的初始化操作,包括扫描组件、加载Bean定义等。
6、Spring应用程序上下文初始化: DispatcherServlet加载Spring应用程序上下文后,Spring会根据配置文件或者注解来初始化所有的Bean,并建立Bean之间的依赖关系。
7、处理请求: 当Tomcat接收到HTTP请求时,DispatcherServlet会根据请求的URL将请求分发给合适的Controller处理。Controller会处理请求,并调用相应的Service或者DAO层来完成业务逻辑。
总的来说,Tomcat作为Servlet容器,通过加载Spring提供的DispatcherServlet来启动和管理Spring工程。Spring的DispatcherServlet负责接收HTTP请求并分发到相应的处理器,从而启动Spring应用程序。
如果Spring bean 没有无参构造方法
会选哪个构造方法
会选哪个构造方法
在Spring容器中实例化Bean时,如果Bean的类没有无参构造方法,Spring会尝试使用带有参数的构造方法进行实例化。Spring会选择匹配参数的构造方法来实例化Bean。如果存在多个带有参数的构造方法,Spring会尝试通过类型匹配来选择合适的构造方法。
如果有多个构造方法可以匹配,但没有一个明显的匹配,Spring将抛出UnsatisfiedDependencyException异常,表示无法确定要使用的构造方法。
如果有多个构造方法可以匹配,但没有一个明显的匹配,Spring将抛出UnsatisfiedDependencyException异常,表示无法确定要使用的构造方法。
IOC AOP的理解
IOC是一种设计思想,它反转了传统程序中对象的创建和管理方式。在传统的编程中,我们通过new关键字主动创建对象并维护它们的生命周期。而在IOC中,对象的创建和管理被交由容器负责。Spring框架是一个经典的IOC容器,通过XML配置、Java注解或者Java配置,它能够管理对象的生命周期、依赖关系和对象之间的引用。开发者只需关注对象的定义和它们之间的关系,而不需要直接负责对象的创建和销毁。
AOP是一种编程范式,它允许我们模块化横切关注点,例如日志、事务、安全等,与业务逻辑分离。在传统的面向对象编程中,我们主要关注类的设计和组织,而AOP关注的是程序执行过程中横切关注点的处理。AOP通过切面、连接点、通知和切点等概念实现。切面是横切关注点的模块化,通知是在切面的某个连接点上执行的动作,连接点是程序执行的点,切点定义了连接点的集合。Spring框架提供了AOP支持,允许我们通过配置或注解的方式,将横切关注点织入到应用程序中,从而实现更好的模块化和可维护性。主要通过jdklib或cglib动态代理实现
Spring有哪些设计模式,和相应的理解
工厂模式(Factory Pattern):
Spring应用: Spring使用工厂模式通过BeanFactory或ApplicationContext创建和管理Bean实例。
理解: 工厂模式将对象的创建过程封装在一个工厂类中,客户端通过工厂类获取所需的对象,而不需要了解对象的具体创建细节。
Spring应用: Spring使用工厂模式通过BeanFactory或ApplicationContext创建和管理Bean实例。
理解: 工厂模式将对象的创建过程封装在一个工厂类中,客户端通过工厂类获取所需的对象,而不需要了解对象的具体创建细节。
单例模式(Singleton Pattern):
Spring应用: Spring默认情况下是单例管理Bean的,即在容器中一个Bean只有一个实例。
理解: 单例模式确保一个类只有一个实例,并提供一个全局访问点。在Spring中,单例Bean通常用于那些无状态的、可共享的组件。
Spring应用: Spring默认情况下是单例管理Bean的,即在容器中一个Bean只有一个实例。
理解: 单例模式确保一个类只有一个实例,并提供一个全局访问点。在Spring中,单例Bean通常用于那些无状态的、可共享的组件。
代理模式(Proxy Pattern):
Spring应用: Spring AOP(面向切面编程)中使用代理模式来实现横切关注点。
理解: 代理模式允许一个对象代表另一个对象进行控制访问。在Spring AOP中,代理对象织入了横切关注点,实现了对原始对象的增强。
Spring应用: Spring AOP(面向切面编程)中使用代理模式来实现横切关注点。
理解: 代理模式允许一个对象代表另一个对象进行控制访问。在Spring AOP中,代理对象织入了横切关注点,实现了对原始对象的增强。
观察者模式(Observer Pattern):
Spring应用: Spring的事件机制使用了观察者模式,如ApplicationEvent和ApplicationListener。
理解: 观察者模式定义了一种一对多的依赖关系,使得一个对象的状态变化会通知所有依赖于它的对象。
Spring应用: Spring的事件机制使用了观察者模式,如ApplicationEvent和ApplicationListener。
理解: 观察者模式定义了一种一对多的依赖关系,使得一个对象的状态变化会通知所有依赖于它的对象。
模板模式(Template Pattern):
Spring应用: Spring的JdbcTemplate和RestTemplate等使用了模板模式。
理解: 模板模式定义了一个算法的骨架,将某些步骤延迟到子类中实现。在Spring中,模板模式通过定义通用的算法骨架,将具体的实现交给子类或回调方法。
Spring应用: Spring的JdbcTemplate和RestTemplate等使用了模板模式。
理解: 模板模式定义了一个算法的骨架,将某些步骤延迟到子类中实现。在Spring中,模板模式通过定义通用的算法骨架,将具体的实现交给子类或回调方法。
策略模式(Strategy Pattern):
Spring应用: Spring的@Qualifier注解和@Primary注解使用了策略模式。
理解: 策略模式定义了一族算法,将它们封装起来,并使它们可以互相替换。在Spring中,策略模式用于选择特定的实现或配置。
Spring应用: Spring的@Qualifier注解和@Primary注解使用了策略模式。
理解: 策略模式定义了一族算法,将它们封装起来,并使它们可以互相替换。在Spring中,策略模式用于选择特定的实现或配置。
springboot的优缺点
优点:
快速开发: Spring Boot采用了约定大于配置的理念,提供了很多默认配置,可以快速搭建和开发应用,减少了开发者的工作量。
自动配置: Spring Boot通过自动配置根据项目的依赖和约定自动配置应用程序,减少了手动配置的工作。
内嵌服务器: Spring Boot内置了常用的Servlet容器(如Tomcat、Jetty),简化了应用的部署过程,可以将应用打包成可执行的JAR文件。
微服务支持: Spring Boot天然地支持构建和部署微服务架构,与Spring Cloud等配合使用,方便微服务的开发和管理。
丰富的生态系统: Spring Boot基于Spring框架,继承了Spring的丰富生态系统,可以方便地集成各种插件、中间件和其他库。
良好的文档和社区支持: Spring Boot拥有广泛的文档和强大的社区支持,开发者可以轻松地获取到大量的学习资源和问题解答。
快速开发: Spring Boot采用了约定大于配置的理念,提供了很多默认配置,可以快速搭建和开发应用,减少了开发者的工作量。
自动配置: Spring Boot通过自动配置根据项目的依赖和约定自动配置应用程序,减少了手动配置的工作。
内嵌服务器: Spring Boot内置了常用的Servlet容器(如Tomcat、Jetty),简化了应用的部署过程,可以将应用打包成可执行的JAR文件。
微服务支持: Spring Boot天然地支持构建和部署微服务架构,与Spring Cloud等配合使用,方便微服务的开发和管理。
丰富的生态系统: Spring Boot基于Spring框架,继承了Spring的丰富生态系统,可以方便地集成各种插件、中间件和其他库。
良好的文档和社区支持: Spring Boot拥有广泛的文档和强大的社区支持,开发者可以轻松地获取到大量的学习资源和问题解答。
缺点:
学习曲线: 对于初学者来说,Spring Boot的学习曲线可能相对陡峭,因为需要理解Spring框架的基础知识。
过于自动化: 对于一些开发者来说,Spring Boot的自动化特性可能有时会导致难以定制和调整,尤其在一些非常定制化的场景下。
应用大小: 由于Spring Boot内嵌了Servlet容器等组件,打包的应用可能相对较大,尤其在需要构建独立的可执行JAR文件时。
不适合所有项目: 对于小型和简单的项目,Spring Boot可能提供了过多的功能,可能不是最佳的选择。选择使用Spring Boot需要根据具体项目需求来评估是否合适。
学习曲线: 对于初学者来说,Spring Boot的学习曲线可能相对陡峭,因为需要理解Spring框架的基础知识。
过于自动化: 对于一些开发者来说,Spring Boot的自动化特性可能有时会导致难以定制和调整,尤其在一些非常定制化的场景下。
应用大小: 由于Spring Boot内嵌了Servlet容器等组件,打包的应用可能相对较大,尤其在需要构建独立的可执行JAR文件时。
不适合所有项目: 对于小型和简单的项目,Spring Boot可能提供了过多的功能,可能不是最佳的选择。选择使用Spring Boot需要根据具体项目需求来评估是否合适。
Srping boot 不同环境下使用不同类用什么注解
spring.profiles.active
Spring cloud Spring cloud Alibaba全部组件的对比
Springboot 条件注解了解么
@ConditionalOnClass: 当类路径下存在指定的类时,才会创建Bean或激活配置
@ConditionalOnMissingClass: 当类路径下不存在指定的类时,才会创建Bean或激活配置。
@ConditionalOnProperty: 当指定的属性存在并且值满足条件时,才会创建Bean或激活配置。
@ConditionalOnExpression: 当指定的SpEL表达式为true时,才会创建Bean或激活配置。
拦截器和过滤器触发的时机
过滤器是Servlet规范定义的一种用于在请求到达Servlet之前或响应离开Servlet之后执行过滤任务的组件。过滤器的生命周期由Servlet容器管理。以下是过滤器的触发时机:
请求到达Servlet之前: 在请求到达Servlet之前,过滤器可以执行一些预处理任务,例如身份验证、日志记录等。doFilter方法用于实际过滤操作。
响应离开Servlet之后: 在Servlet处理完请求并生成响应后,响应离开Servlet之前,过滤器可以执行一些后处理任务。
请求到达Servlet之前: 在请求到达Servlet之前,过滤器可以执行一些预处理任务,例如身份验证、日志记录等。doFilter方法用于实际过滤操作。
响应离开Servlet之后: 在Servlet处理完请求并生成响应后,响应离开Servlet之前,过滤器可以执行一些后处理任务。
拦截器是Spring MVC框架提供的组件,用于在请求到达控制器之前或离开控制器之后执行拦截任务。拦截器的生命周期由Spring容器管理。以下是拦截器的触发时机:
请求到达控制器之前: 在请求到达Spring MVC控制器之前,拦截器可以执行一些预处理任务。preHandle方法用于实际拦截操作。
控制器处理完请求后: 在控制器处理完请求后,返回视图之前,拦截器可以执行一些后处理任务。postHandle方法用于实际拦截操作。
视图渲染完毕后: 在视图渲染完毕后,即响应已经生成并准备发送到客户端时,拦截器可以执行一些最终的任务。afterCompletion方法用于实际拦截操作。
请求到达控制器之前: 在请求到达Spring MVC控制器之前,拦截器可以执行一些预处理任务。preHandle方法用于实际拦截操作。
控制器处理完请求后: 在控制器处理完请求后,返回视图之前,拦截器可以执行一些后处理任务。postHandle方法用于实际拦截操作。
视图渲染完毕后: 在视图渲染完毕后,即响应已经生成并准备发送到客户端时,拦截器可以执行一些最终的任务。afterCompletion方法用于实际拦截操作。
总体而言,过滤器是在Servlet规范层面实现的,而拦截器是在Spring MVC框架中实现的。过滤器的生命周期由Servlet容器管理,而拦截器的生命周期由Spring容器管理。拦截器提供了更细粒度的控制和更多的钩子方法,但在一些情况下,过滤器可能更适合处理一些通用的Web请求过滤任务。
过滤器和拦截器的区别
1、拦截器是基于java的反射机制的,而过滤器是基于函数回调。
2、拦截器不依赖于servlet容器,过滤器依赖与servlet容器。
3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
6、拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
2、拦截器不依赖于servlet容器,过滤器依赖与servlet容器。
3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
6、拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
同类方法调用事务失效的原因
Spring中通过代理实现事务管理。如果同一个类内部的方法直接调用,而不是通过代理对象调用,事务可能会失效。因为代理对象无法截获同类方法的调用,从而无法处理事务。
并发
sync
作用对象
普通同步方法,锁是当前实例对象
静态同步方法,锁是当前类的class对象
同步方法块,锁是括号里面的对象
底层原理
基于进入和退出Monitor对象实现
对象头
Mark Word(标记字段)、Klass Pointer(类型指针)
实现轻量级锁和偏向锁的关键
锁的优化
jdk1.6之前
底层monitor会阻塞和唤醒线程,线程的阻塞和唤醒需要CPU从“用户态”转为“内核态”
1.6之后
无锁
底层是通过CAS实现的
偏向锁
在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低,引进了偏向锁
这个锁会偏向于第一个获得它的线程,会在对象头存储锁偏向的线程ID
以后该线程进入和退出同步块时只需要检查是否为偏向锁、锁标志位以及ThreadID即可
原理
1. 虚拟机将会把对象头中的标志位设为“01”,即偏向模式。
2. 同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中 ,
如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作,偏向锁的效率高。
2. 同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中 ,
如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作,偏向锁的效率高。
偏向锁的撤销
1. 偏向锁的撤销动作必须等待全局安全点
2. 暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态
3. 撤销偏向锁,恢复到无锁(标志位为 01)或轻量级锁(标志位为 00)的状态
指令
偏向锁在Java 6之后是默认启用的,但在应用程序启动几秒钟(4s)之后才激活,可以使用-XX:BiasedLockingStartupDelay=0 参数关闭延迟,
如果确定应用程序中所有锁通常情况下处于竞争状态,可以通过XX:-UseBiasedLocking=false 参数关闭偏向锁。
如果确定应用程序中所有锁通常情况下处于竞争状态,可以通过XX:-UseBiasedLocking=false 参数关闭偏向锁。
轻量级锁
目的
在多线程交替执行同步块的情况下,尽量避免重量级锁引起的性能消耗,但是如果多个线程在同一时刻进入临界区,会导致轻量级锁膨胀升级重量级锁,所以轻量级锁的出现并非是要替代重量级锁
原理
当关闭偏向锁功能或者多个线程竞争偏向锁导致偏向锁升级为轻量级锁,则会尝试获取轻量级锁
将对象的Mark Word复制到栈帧中的Lock Recod中。Mark Word更新为指向Lock Record的指针。
好处
在多线程交替执行同步块的情况下,可以避免重量级锁引起的性能消耗。
释放
CAS操作
1. 取出在获取轻量级锁保存在Displaced Mark Word中的数据。
2. 用CAS操作将取出的数据替换当前对象的Mark Word中,如果成功,则说明释放锁成功。
3. 如果CAS操作替换失败,说明有其他线程尝试获取该锁,则需要将轻量级锁需要膨胀升级为重量级锁。
2. 用CAS操作将取出的数据替换当前对象的Mark Word中,如果成功,则说明释放锁成功。
3. 如果CAS操作替换失败,说明有其他线程尝试获取该锁,则需要将轻量级锁需要膨胀升级为重量级锁。
对于轻量级锁,其性能提升的依据是“对于绝大部分的锁,在整个生命周期内都是不会存在竞争的”,如果打破这个依据则除了互斥的开销外,
还有额外的CAS操作,因此在有多线程竞争的情况下,轻量级锁比重量级锁更慢。
还有额外的CAS操作,因此在有多线程竞争的情况下,轻量级锁比重量级锁更慢。
自旋锁
自旋锁原理
monitor实现锁的时候,知道monitor会阻塞和唤醒线程,线程的阻塞和唤醒需要CPU从用户态转为内核态,频繁的阻塞和唤醒对CPU来说是一件负担很重的工作
自旋等待不能代替阻塞,且先不说对处理器数量的要求,自旋等待本身虽然避免了线程切换的开销,但它是要占用处理器时间的
如果锁被占用的时间很短,自旋等待的效果就会非常好,
如果锁被占用的时间很长。那么自旋的线程只会白白消耗处理器资源,而不会做任何有用的工作,反而会带来性能上的浪费。
如果锁被占用的时间很长。那么自旋的线程只会白白消耗处理器资源,而不会做任何有用的工作,反而会带来性能上的浪费。
自旋等待的时间必须要有一定的限度,如果自旋超过了限定的次数仍然没有成功获得锁,就应当使用传统的方式去挂起线程
自旋次数的默认值是10次,用户可以使用参数-XX : PreBlockSpin来更改
自旋锁在JDK 1.4.2中就已经引入 ,只不过默认是关闭的,可以使用-XX:+UseSpinning参数来开启,在JDK 6中 就已经改为默认开启了
适应性自旋锁
JDK 6中引入了自适应的自旋锁。自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定
如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,
进而它将允许自旋等待持续相对更长的时间,比如100次循环。
进而它将允许自旋等待持续相对更长的时间,比如100次循环。
如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源
锁消除
指虚拟机即时编译器(JIT)在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除
判定依据
来源于逃逸分析的数据支持,如果判断在一段代码中,堆上的所有数据都不会逃逸出去从而被其他线程访问到,那就可以把它们当做栈上数据对待,认为它们是线程私有的,同步加锁自然就无须进行
锁粗化
volatile关键字
CAS原理,缺陷如何解决
ABA问题 ,加版本号
CopyOnArrayList、ConcurrentHashMap如何保证线程安全的
项目中哪里用到多线程,为什么要用?
并发处理: 当有多个任务需要同时执行,且相互之间不影响或依赖的时候,可以使用多线程来提高系统的并发处理能力,提高效率。
异步编程: 多线程可以用于处理异步任务,例如在用户界面中进行网络请求、文件读写等操作,以避免阻塞用户界面。
任务分解: 将大任务分解为多个小任务,每个任务由一个线程独立执行,可以提高任务的并行度,加速任务的完成。
提高系统性能: 多线程可以利用多核处理器,提高系统整体的性能。
异步编程: 多线程可以用于处理异步任务,例如在用户界面中进行网络请求、文件读写等操作,以避免阻塞用户界面。
任务分解: 将大任务分解为多个小任务,每个任务由一个线程独立执行,可以提高任务的并行度,加速任务的完成。
提高系统性能: 多线程可以利用多核处理器,提高系统整体的性能。
你对于多线程的理解?
多线程是一种并发编程的手段,它允许程序同时执行多个独立的线程,每个线程都有自己的执行路径。多线程可以使得程序在某些情况下更加高效,充分利用计算资源,提高系统的响应速度和吞吐量。
然而,多线程也带来了一些挑战,如竞态条件、死锁、资源竞争等问题。因此,在使用多线程时需要注意线程安全性、同步机制等问题,以保证程序的正确性和稳定性。
然而,多线程也带来了一些挑战,如竞态条件、死锁、资源竞争等问题。因此,在使用多线程时需要注意线程安全性、同步机制等问题,以保证程序的正确性和稳定性。
JDK19 新推出的协程相比JDK8的线程有哪些优势?
JDK 8 线程:
使用基于线程的并发模型,每个线程对应一个执行路径。
线程切换会涉及上下文切换,可能会带来一些性能开销。
线程通常需要占用一定的内存资源。
JDK 19 协程:
协程是一种轻量级的线程,不需要创建新的线程。
协程的切换是用户级的,不涉及内核级别的上下文切换,避免了一些性能开销。
协程可以更灵活地管理并发,不需要关心线程的创建和销毁。
优势和选择取决于具体的使用场景:
协程适用于高并发、高吞吐量的场景,特别是在IO密集型任务中。
线程适用于CPU密集型任务,或者需要与外部系统交互的任务。
使用基于线程的并发模型,每个线程对应一个执行路径。
线程切换会涉及上下文切换,可能会带来一些性能开销。
线程通常需要占用一定的内存资源。
JDK 19 协程:
协程是一种轻量级的线程,不需要创建新的线程。
协程的切换是用户级的,不涉及内核级别的上下文切换,避免了一些性能开销。
协程可以更灵活地管理并发,不需要关心线程的创建和销毁。
优势和选择取决于具体的使用场景:
协程适用于高并发、高吞吐量的场景,特别是在IO密集型任务中。
线程适用于CPU密集型任务,或者需要与外部系统交互的任务。
lock和synchronized有啥区别吗
1.synchronized是关键字,Lock是接口;
2.synchronized是隐式的加锁,lock是显式的加锁;
3.synchronized可以作用于方法上,lock只能作用于方法块;
4.synchronized底层采用的是objectMonitor,lock采用的AQS;
5.synchronized是阻塞式加锁,lock是非阻塞式加锁支持可中断式加锁,支持超时时间的加锁;
6.synchronized在进行加锁解锁时,只有一个同步队列和一个等待队列, lock有一个同步队列,可以有多个等待队列;
7.synchronized只支持非公平锁,lock支持非公平锁和公平锁;
8.synchronized使用了object类的wait和notify进行等待和唤醒, lock使用了condition接口进行等待和唤醒(await和signal);
9.lock支持个性化定制, 使用了模板方法模式,可以自行实现lock方法;
2.synchronized是隐式的加锁,lock是显式的加锁;
3.synchronized可以作用于方法上,lock只能作用于方法块;
4.synchronized底层采用的是objectMonitor,lock采用的AQS;
5.synchronized是阻塞式加锁,lock是非阻塞式加锁支持可中断式加锁,支持超时时间的加锁;
6.synchronized在进行加锁解锁时,只有一个同步队列和一个等待队列, lock有一个同步队列,可以有多个等待队列;
7.synchronized只支持非公平锁,lock支持非公平锁和公平锁;
8.synchronized使用了object类的wait和notify进行等待和唤醒, lock使用了condition接口进行等待和唤醒(await和signal);
9.lock支持个性化定制, 使用了模板方法模式,可以自行实现lock方法;
场景
synchronized简单并发
lock 更细力度的锁控制,使用更灵活
lock 更细力度的锁控制,使用更灵活
加锁的话是什么情况下需要加锁呢?
进程、线程
java多线程如何实现线程同步,手写多线程死锁代码
进程和线程的区别
Java线程和linux线程什么关系,是一对一还是一对多
每个Java线程通常会映射到一个底层的操作系统线程(Linux线程)。
有进程了为什么还要有线程
1.提高并发性:线程允许在同一个进程内同时执行多个任务,提高了程序的并发性和性能。这对于多核处理器来说尤为重要,因为它们可以同时执行多个线程,提高了系统的利用率。
2.资源共享:线程可以更轻松地共享内存,这对于多个任务之间的数据共享和通信非常有用。在多线程应用程序中,可以使用全局变量或线程间的消息传递来实现数据共享。
3.系统开销:创建和销毁进程比线程的开销更大,因为进程拥有独立的资源。因此,在某些情况下,使用线程可以减少系统开销。
4.任务划分:线程使得任务的划分和管理更加灵活,可以根据需要创建和销毁线程,而不必创建新的进程。
2.资源共享:线程可以更轻松地共享内存,这对于多个任务之间的数据共享和通信非常有用。在多线程应用程序中,可以使用全局变量或线程间的消息传递来实现数据共享。
3.系统开销:创建和销毁进程比线程的开销更大,因为进程拥有独立的资源。因此,在某些情况下,使用线程可以减少系统开销。
4.任务划分:线程使得任务的划分和管理更加灵活,可以根据需要创建和销毁线程,而不必创建新的进程。
什么是线程安全
线程安全是指在多线程环境下,一个操作或一系列操作能够被正确执行,而不会导致数据不一致、状态异常或者程序中断。具体而言,线程安全的代码能够确保在并发执行时,不会出现数据竞争、死锁、活锁等并发问题。保障数据的原子性,可见性和有序性
wait notify为什么放在while循环里
在使用 wait 和 notify 时,通常会将 wait 放在一个 while 循环中,而不是 if 语句中。这是为了防止虚假唤醒,即在没有调用 notify 或 notifyAll 的情况下,线程也可能从等待状态中被唤醒。通过放在 while 循环中,可以确保线程在被唤醒后重新检查条件,从而避免不正确的执行。
wait notify 为什么在object类下
wait 和 notify: wait 和 notify 方法是用于实现线程间通信的机制,因此它们被定义在 Object 类中。这是因为所有的Java对象都可以被用作锁,而线程间通信通常涉及到对共享资源的锁定和释放。
sleep: 相比之下,sleep 方法是 Thread 类的静态方法,用于暂停当前线程的执行一段时间。这是因为 sleep 是与线程的时间控制相关的,而不是与特定的对象或锁有关。
sleep: 相比之下,sleep 方法是 Thread 类的静态方法,用于暂停当前线程的执行一段时间。这是因为 sleep 是与线程的时间控制相关的,而不是与特定的对象或锁有关。
线程池
线程池参数 以及参数作用
corePoolSize线程池中的常驻核心线程数
maximumPoolSize:线程池能够容纳同时执行的最大线程数,此值大于等于1
keepAliveTime:多余的空闲线程存活时间,当空间时间达到keepAliveTime值时,多余的线程会被销毁
直到剩下corePoolSize个线程为止
直到剩下corePoolSize个线程为止
默认情况下:只有当线程池中的线程数大于corePoolSize时keepAliveTime才会起作用,知道线程中的线程
数不大于corepoolSIze。
数不大于corepoolSIze。
Unit:keepAliveTime的单位
WorkQueue:任务队列,被提交但尚未被执行的任务
ThreadFactory:表示生成线程池中工作线程的线程工厂,用户创建新线程,一搬用默认即可
Handler:拒绝策略,表示当线程队列满了并且工作线程大于等于线程池的最大线程数
(maximumPoolSize)时如何来拒绝请求执行的runnable的策略
(maximumPoolSize)时如何来拒绝请求执行的runnable的策略
你刚刚也提到了多线程,你一般是怎么创建线程的呢?
线程池的话你用过什么样的线程池
你能讲一下线程池整个一个过程么?就是我的一个任务一直往里加,他会进入一个怎样的一个过程呢
线程池的队列满了怎么外理
你刚说到动态线程池,你们是如何做的?
线程池如何销毁线程的
在 runWorker 方法中,工作线程首先通过 getTask 方法获取任务,然后执行任务的 run 方法。任务执行完毕后,会调用 afterExecute 方法。
在 afterExecute 方法中,会检查线程池的终止状态,并根据终止状态来决定是否进行线程的回收。如果线程池需要进行回收,会调用 processWorkerExit 方法,该方法包含了线程的退出逻辑。
在 afterExecute 方法中,会检查线程池的终止状态,并根据终止状态来决定是否进行线程的回收。如果线程池需要进行回收,会调用 processWorkerExit 方法,该方法包含了线程的退出逻辑。
动态修改线程池参数
线程池的名字如何定义的
Redis
数据量很大的时候redis扩容怎么处理不会崩溃
redis为什么这么快
redis如何保证事务原子性的
分布式锁
为什么用redis
redisson为什么好
redis能否做存储(不支持事务、持久化丢数据)
zset数据结构,为什么这么设计
redisRDB能保证数据一致性吗
zset数据结构、与java Treeset比较
redis缓存失效机制,定时失效如何实现
redis常见数据结构,String性能好的原因,有序集合数据结构,压缩列表结构
redission如何实现分布式锁
如果上锁的进程挂掉了是不是锁就一直存在了
锁设计了过期时间还会有什么问题
redis中如果一个key过大,如果删除掉会有什么问题,,比如删除掉一个特别大的zset
Redis相关 (IO多路复用,什么时候选select/epoll?
如果你是往redis中丢一个数据进去假设redis挂了,我重启他,这个数据还在吗
你用分布式锁的话,假设锁住了一个代码在里面执行一个任务,假设这个任务执行的时间很长会不会导致锁过期给别的线程抢占,但这个任务还没结束,我不想让他释放锁,有什么策略能实现这样的功能呢?
跳表查找过程,跳表节点包含什么内容
布隆过滤器原理
布隆过滤器怎么提供准确度
Redis的持久化的方式?AOF和RDB是什么?结合实际思考有什么优缺点?
Redis的分布式锁如何设计,有什么问题
Redis大key如何解决
如何解决热key问题
缓存 一致性如何保障
redis 部署模式
redis主从复制原理
io多路复用
epoll poll 区别
网络
rpc原理
消息队列
kafka
集群如何保证顺序写入读取
只使用一个partition,或者指定partition
发送者、消费者、kafka服务自己如何做到不丢数据
生产者
异步发送:实现回调方法,消息发送失败时记录日志,或者重新发送,最终确保消息能够成功发送
消息重试机制
acks = all
消费者
禁用自动提交偏移量,改为手动
服务本身:leo hw isr 机制
异步发送:实现回调方法,消息发送失败时记录日志,或者重新发送,最终确保消息能够成功发送
消息重试机制
acks = all
消费者
禁用自动提交偏移量,改为手动
服务本身:leo hw isr 机制
消费者如何做到不重复消费
手动提交
启用Kafka幂等性和事务
自身幂等
启用Kafka幂等性和事务
自身幂等
partition的副本机制项目中是如何使用的
同一类型数据在同一partition中
项目中如何解决kafka的重复消息
自身幂等
项自为什么kaka而不是其他的?kafka和rocketmg有什么区别,适用场景
高性能,高吞吐
kafka如何保证一致性和可用性
一致性:
1、分区有序性:Kafka 保证单个分区内的事件是有序的。这意味着,对于给定的分区,消息将按照它们被写入的顺序被消费者读取。
2、Exactly-Once 语义:Kafka 支持 Exactly-Once 语义,这意味着每条消息只会被消费者处理一次,即使在发生故障的情况下也是如此。这是通过使用事务和幂等性写入来实现的。
3、副本同步:Kafka 使用多副本架构来确保数据的持久性和一致性。每个分区都有一个领导者(Leader)和多个追随者(Follower)。所有的读写操作都通过领导者进行,追随者定期从领导者复制数据。如果领导者失败,其中一个追随者将被选举为新的领导者。
可用性:
1、多副本架构:如上所述,Kafka 的多副本架构提供了高可用性。如果某个 Broker 宕机,该 Broker 上的分区在其他机器上的副本将仍然可用。
2、Broker 容错:Kafka 集群中的 Broker 是可以容错的,这意味着即使一些 Broker 不可用,集群仍然可以继续运行。Kafka 通过将分区复制到多个 Broker 上来实现这一点。
3、自动故障转移:如果领导者 Broker 宕机,Kafka 会自动从追随者中选举一个新的领导者。这确保了分区的持续可用性。
4、配置调优:通过调整 Kafka 的配置参数,如副本因子(replication factor)和分区数(number of partitions),可以进一步优化其可用性和一致性。
1、分区有序性:Kafka 保证单个分区内的事件是有序的。这意味着,对于给定的分区,消息将按照它们被写入的顺序被消费者读取。
2、Exactly-Once 语义:Kafka 支持 Exactly-Once 语义,这意味着每条消息只会被消费者处理一次,即使在发生故障的情况下也是如此。这是通过使用事务和幂等性写入来实现的。
3、副本同步:Kafka 使用多副本架构来确保数据的持久性和一致性。每个分区都有一个领导者(Leader)和多个追随者(Follower)。所有的读写操作都通过领导者进行,追随者定期从领导者复制数据。如果领导者失败,其中一个追随者将被选举为新的领导者。
可用性:
1、多副本架构:如上所述,Kafka 的多副本架构提供了高可用性。如果某个 Broker 宕机,该 Broker 上的分区在其他机器上的副本将仍然可用。
2、Broker 容错:Kafka 集群中的 Broker 是可以容错的,这意味着即使一些 Broker 不可用,集群仍然可以继续运行。Kafka 通过将分区复制到多个 Broker 上来实现这一点。
3、自动故障转移:如果领导者 Broker 宕机,Kafka 会自动从追随者中选举一个新的领导者。这确保了分区的持续可用性。
4、配置调优:通过调整 Kafka 的配置参数,如副本因子(replication factor)和分区数(number of partitions),可以进一步优化其可用性和一致性。
业务代码修改mysql数据库后发送kafka消息然后事务回滚,但消息已经发了咋办
两阶段提交(2PC): 使用两阶段提交协议,确保数据库和 Kafka 操作在事务中同时提交或同时回滚。这种方法可以确保数据的一致性,但也带来了一些复杂性,并且可能影响性能。
消息表: 在 MySQL 中创建一个消息表,记录待发送到 Kafka 的消息。在业务代码中,将消息插入到消息表中,然后通过异步任务或定时任务发送消息到 Kafka。如果事务回滚,删除消息表中对应的记录。这种方法在业务和消息发送之间引入了消息表作为缓冲,以确保只有在数据库事务提交后才会发送消息。
使用 Kafka 事务:
Kafka 支持事务性生产者,允许你将多个操作(如数据库更新和 Kafka 消息发送)组合到一个事务中。这样,如果数据库更新失败,你可以回滚 Kafka 消息的发送,确保两者的一致性。
要使用 Kafka 事务,你需要设置生产者的事务 ID,并在发送消息之前开启事务。如果所有操作都成功,则提交事务;如果任何操作失败,则回滚事务。
使用数据库触发器或后处理任务:
在数据库中,你可以设置触发器或后处理任务来监视特定的表或操作。当检测到某个操作(如 UPDATE 或 DELETE)并且该操作被回滚时,触发器或任务可以负责从 Kafka 中删除相应的消息。
补偿事务:
如果消息已经被发送并且数据库事务回滚,你可以设计一个补偿事务来纠正不一致性。补偿事务会检测到不一致状态,并执行必要的操作来恢复数据的一致性。
消息表: 在 MySQL 中创建一个消息表,记录待发送到 Kafka 的消息。在业务代码中,将消息插入到消息表中,然后通过异步任务或定时任务发送消息到 Kafka。如果事务回滚,删除消息表中对应的记录。这种方法在业务和消息发送之间引入了消息表作为缓冲,以确保只有在数据库事务提交后才会发送消息。
使用 Kafka 事务:
Kafka 支持事务性生产者,允许你将多个操作(如数据库更新和 Kafka 消息发送)组合到一个事务中。这样,如果数据库更新失败,你可以回滚 Kafka 消息的发送,确保两者的一致性。
要使用 Kafka 事务,你需要设置生产者的事务 ID,并在发送消息之前开启事务。如果所有操作都成功,则提交事务;如果任何操作失败,则回滚事务。
使用数据库触发器或后处理任务:
在数据库中,你可以设置触发器或后处理任务来监视特定的表或操作。当检测到某个操作(如 UPDATE 或 DELETE)并且该操作被回滚时,触发器或任务可以负责从 Kafka 中删除相应的消息。
补偿事务:
如果消息已经被发送并且数据库事务回滚,你可以设计一个补偿事务来纠正不一致性。补偿事务会检测到不一致状态,并执行必要的操作来恢复数据的一致性。
kafka的rebalance机制
(1)组内成员发生变更;
(2)订阅topic发生变更;
(3)topic关联的分区数发生变更。
(2)订阅topic发生变更;
(3)topic关联的分区数发生变更。
mq实现定时发送
0 条评论
下一页