20250416-携程
一半八股一半项目无手撕,总结不会的问题,已OC
1.RocketMQ怎么确保消息仅消费一次(面试官的意思这几个常用的MQ仅仅RocketMQ通过拓展功能可以保证唯一消费一次):一开始回答了消息的幂等性用唯一id保证,还可以把对应消费的状态存在redis里面还有mysql里面分多级缓存,如果对应id还是消费中的状态就可以进行重新消费,然后面试官的意思是RocketMQ通过拓展功能可以保证唯一消费一次(这个确实不太懂)
RocketMQ 的 Exactly-Once 投递语义,就是用于解决幂等问题。Exactly-Once 是指发送到消息系统的消息只能被消费端处理且仅处理一次,即使生产端重试消息发送导致某消息重复投递,该消息在消费端也只被消费一次。
最佳的幂等处理方式还是需要有一个唯一的业务标识,虽然每条消息都有 MessageId,但是不建议用 MessageId 来做幂等判断,在发送消息的时候,可以为每条消息设置一个 MessageKey,这个 MessageKey 就可以用来做业务的唯一标识。
参考:https://www.nowcoder.com/discuss/353148811591753728?sourceSSR=search
*************************************************
使用文档可以参考https://github.com/Jaskey/RocketMQDedupListener ,
2.CMS垃圾回收过程,重新标记阶段是怎么实现的,怎么确保效率(之前没了解过增量并发标记、satb)
CMS采用的是增量并发标记,G1中使用的是SATB(Snapshot-At-The-Beginning)算法
漏标问题:三色标记
(1)已被遍历标记过的黑色对象重新引用了该白色对象;
(2)删除了全部从灰色对象到该白色对象的直接或间接引用。
处理漏标
(1)CMS 是基于增量更新来做并发标记的。增量更新要破坏的是第一个条件,当黑色对象插入新的指向白色对象的引用关系时,就将这个新插入的引用记录下来,等并发扫描结束之后,再将这些记录过的引用关系中的黑色对象为根,重新扫描一次。这可以简化理解为,黑色对象一旦新插入了指向白色对象的引用之后,它就变回灰色对象了。
(2)G1 是基于原始快照来做并发标记的。原始快照要破坏的是第二个条件,当灰色对象要删除指向白色对象的引用关系时,就将这个要删除的引用记录下来(使用一个快照记录标记过程中新分配的对象),在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描一次。这也可以简化理解为,无论引用关系删除与否,都会按照刚刚开始扫描那一刻的对象图快照来进行搜索。
其他:CMS和G1的区别
(1)在 G1 收集器出现之前的所有其他收集器,包括 CMS 在内,垃圾收集的目标范围要么是整个新生代(Minor GC),要么就是整个老年代(Major GC),再要么就是整个 Java 堆(Full GC)。
(2)G1 把堆内存分割为很多不相关的区域(Region,物理上不连续区分,逻辑上是连续区分 Eden 区、Survivor区,old区),Region 作为单次回收的最小单元,它可以面向堆内存任何部分来组成回收集(Collection Set,一般简称 CSet)进行回收,衡量标准不再是它属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大,这就是 G1 收集器的 Mixed GC模式。
参考:https://blog.csdn.net/longool/article/details/140736740
3.JDK21引入的虚拟线程的概念:有点模糊,协程我以为不会问没背
传统线程
(1)绿色线程-定义:早期 Java 使用的一种线程模型,由 JVM 完成调度和管理,属于 M:1 线程映射模型。缺点:由于无法充分利用硬件性能,且实现复杂,最终被淘汰。
(2)平台线程-定义:Java 1.2 之后采用的线程模型,依赖于操作系统的支持,属于 1:1 线程映射模型。优点:执行效率高,但开启和关闭线程的资源消耗较大。
存在问题
在内存有限的情况下,使用传统平台线程处理大量 IO 密集型请求,线程数量有限,会导致 CPU 利用率低。
虚拟线程是轻量级线程(类似于 Go 中的 “协程(Goroutine)”),是由Java虚拟机调度,而不是操作系统。虚拟线程占用空间小,任务切换开销几乎可以忽略不计,因此可以极大量地创建和使用。JVM 使用 M:N 来完成虚拟线程与本地线程的映射。
虚拟线程和线程池的异同
看上去虚拟线程和线程池有类似之处,都是利用M个内核线程,完成N个任务,而避免平台线程频繁的创建和销毁。但他们是有本质区别的:
(1)线程池中的正在执行的任务只有到任务执行完成后,才会释放平台线程,如果某个任务在执行过程中发生IO阻塞也不会被挂起执行其他任务。
(2)虚拟线程中运行的代码调用阻塞I/O操作时,Java运行时会挂起虚拟线程,然后切换到另一个可执行的虚拟线程,直到它可以恢复为止。
参考:https://blog.csdn.net/tianjindong0804/article/details/135046271
4.分布式不同机器,写数据库了,怎么确保task消息的幂等性和同步更新(这里我回答了mysql的隔离级别,读已提交,还有主从)
分布式事务:使用分布式事务管理器(如XA协议)来确保在多个数据库之间的一致性。
5. 谈谈CompletableFuture(之前没了解过)
https://www.nowcoder.com/discuss/686266738308055040?sourceSSR=search
其他题目
(1)CompletableFuture 和 Future 的区别,返回的是什么,怎么捕获异常
(2)CompletableFuture 里的 all of 方法可以怎么实现(讲了 AtomicInteger 计数,他问还有没有,讲了信号量,他说其实还有 CountDownLatch)
参考链接:https://www.nowcoder.com/feed/main/detail/b9d0579f8bfa46d8b44af7619a6f1b72?sourceSSR=search
1. CompletableFuture
CompletableFuture 是 Java 8 引入的类,用于支持异步编程。它实现了 Future 和 CompletionStage 接口,使得处理异步任务变得更加灵活和强大。
2. Future 和 FutureTask
Future: 是一个接口,表示一个异步计算的结果。它提供了方法来检查计算是否完成、获取结果或取消任务。
FutureTask: 是 Future 的一个实现,允许将计算封装为一个可执行的任务。它可以在一个线程中运行,并可以通过 Future 接口的方法来获取结果。
3. 异步任务
使用 CompletableFuture 可以在后台线程中异步执行任务,而不会阻塞主线程。示例中使用 runAsync() 和 supplyAsync() 方法分别执行没有返回值和有返回值的任务。
4. 任务依赖
CompletableFuture 允许通过链式调用来构建复杂的任务依赖关系。常用的方法有:
thenRun(): 在前一个任务完成后执行另一个任务,不接受参数且没有返回值。
thenAccept(): 在前一个任务完成后消费结果,接受一个参数但没有返回值。
thenApply(): 在前一个任务完成后处理结果,接受一个参数并返回一个新的 CompletableFuture。
5. 并行任务
anyOf(): 允许多个任务并行执行,并返回第一个完成的任务的结果。
allOf(): 允许多个任务并行执行,等所有任务完成后聚合结果。
6. 回调
CompletableFuture 支持在某个任务完成后执行回调方法。常用的方法有:
whenComplete(): 执行回调任务,没有返回值。
handle(): 执行回调任务并返回结果,可以处理异常情况。
6. 业务的动态部署
总结:第一次面试,八股背的深度不够,好多细节遗漏接着面,接着背
1.RocketMQ怎么确保消息仅消费一次(面试官的意思这几个常用的MQ仅仅RocketMQ通过拓展功能可以保证唯一消费一次):一开始回答了消息的幂等性用唯一id保证,还可以把对应消费的状态存在redis里面还有mysql里面分多级缓存,如果对应id还是消费中的状态就可以进行重新消费,然后面试官的意思是RocketMQ通过拓展功能可以保证唯一消费一次(这个确实不太懂)
RocketMQ 的 Exactly-Once 投递语义,就是用于解决幂等问题。Exactly-Once 是指发送到消息系统的消息只能被消费端处理且仅处理一次,即使生产端重试消息发送导致某消息重复投递,该消息在消费端也只被消费一次。
最佳的幂等处理方式还是需要有一个唯一的业务标识,虽然每条消息都有 MessageId,但是不建议用 MessageId 来做幂等判断,在发送消息的时候,可以为每条消息设置一个 MessageKey,这个 MessageKey 就可以用来做业务的唯一标识。
参考:https://www.nowcoder.com/discuss/353148811591753728?sourceSSR=search
*************************************************
使用文档可以参考https://github.com/Jaskey/RocketMQDedupListener ,
2.CMS垃圾回收过程,重新标记阶段是怎么实现的,怎么确保效率(之前没了解过增量并发标记、satb)
CMS采用的是增量并发标记,G1中使用的是SATB(Snapshot-At-The-Beginning)算法
漏标问题:三色标记
(1)已被遍历标记过的黑色对象重新引用了该白色对象;
(2)删除了全部从灰色对象到该白色对象的直接或间接引用。
处理漏标
(1)CMS 是基于增量更新来做并发标记的。增量更新要破坏的是第一个条件,当黑色对象插入新的指向白色对象的引用关系时,就将这个新插入的引用记录下来,等并发扫描结束之后,再将这些记录过的引用关系中的黑色对象为根,重新扫描一次。这可以简化理解为,黑色对象一旦新插入了指向白色对象的引用之后,它就变回灰色对象了。
(2)G1 是基于原始快照来做并发标记的。原始快照要破坏的是第二个条件,当灰色对象要删除指向白色对象的引用关系时,就将这个要删除的引用记录下来(使用一个快照记录标记过程中新分配的对象),在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描一次。这也可以简化理解为,无论引用关系删除与否,都会按照刚刚开始扫描那一刻的对象图快照来进行搜索。
其他:CMS和G1的区别
(1)在 G1 收集器出现之前的所有其他收集器,包括 CMS 在内,垃圾收集的目标范围要么是整个新生代(Minor GC),要么就是整个老年代(Major GC),再要么就是整个 Java 堆(Full GC)。
(2)G1 把堆内存分割为很多不相关的区域(Region,物理上不连续区分,逻辑上是连续区分 Eden 区、Survivor区,old区),Region 作为单次回收的最小单元,它可以面向堆内存任何部分来组成回收集(Collection Set,一般简称 CSet)进行回收,衡量标准不再是它属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大,这就是 G1 收集器的 Mixed GC模式。
参考:https://blog.csdn.net/longool/article/details/140736740
3.JDK21引入的虚拟线程的概念:有点模糊,协程我以为不会问没背
传统线程
(1)绿色线程-定义:早期 Java 使用的一种线程模型,由 JVM 完成调度和管理,属于 M:1 线程映射模型。缺点:由于无法充分利用硬件性能,且实现复杂,最终被淘汰。
(2)平台线程-定义:Java 1.2 之后采用的线程模型,依赖于操作系统的支持,属于 1:1 线程映射模型。优点:执行效率高,但开启和关闭线程的资源消耗较大。
存在问题
在内存有限的情况下,使用传统平台线程处理大量 IO 密集型请求,线程数量有限,会导致 CPU 利用率低。
虚拟线程是轻量级线程(类似于 Go 中的 “协程(Goroutine)”),是由Java虚拟机调度,而不是操作系统。虚拟线程占用空间小,任务切换开销几乎可以忽略不计,因此可以极大量地创建和使用。JVM 使用 M:N 来完成虚拟线程与本地线程的映射。
虚拟线程和线程池的异同
看上去虚拟线程和线程池有类似之处,都是利用M个内核线程,完成N个任务,而避免平台线程频繁的创建和销毁。但他们是有本质区别的:
(1)线程池中的正在执行的任务只有到任务执行完成后,才会释放平台线程,如果某个任务在执行过程中发生IO阻塞也不会被挂起执行其他任务。
(2)虚拟线程中运行的代码调用阻塞I/O操作时,Java运行时会挂起虚拟线程,然后切换到另一个可执行的虚拟线程,直到它可以恢复为止。
参考:https://blog.csdn.net/tianjindong0804/article/details/135046271
4.分布式不同机器,写数据库了,怎么确保task消息的幂等性和同步更新(这里我回答了mysql的隔离级别,读已提交,还有主从)
分布式事务:使用分布式事务管理器(如XA协议)来确保在多个数据库之间的一致性。
5. 谈谈CompletableFuture(之前没了解过)
https://www.nowcoder.com/discuss/686266738308055040?sourceSSR=search
其他题目
(1)CompletableFuture 和 Future 的区别,返回的是什么,怎么捕获异常
(2)CompletableFuture 里的 all of 方法可以怎么实现(讲了 AtomicInteger 计数,他问还有没有,讲了信号量,他说其实还有 CountDownLatch)
参考链接:https://www.nowcoder.com/feed/main/detail/b9d0579f8bfa46d8b44af7619a6f1b72?sourceSSR=search
1. CompletableFuture
CompletableFuture 是 Java 8 引入的类,用于支持异步编程。它实现了 Future 和 CompletionStage 接口,使得处理异步任务变得更加灵活和强大。
2. Future 和 FutureTask
Future: 是一个接口,表示一个异步计算的结果。它提供了方法来检查计算是否完成、获取结果或取消任务。
FutureTask: 是 Future 的一个实现,允许将计算封装为一个可执行的任务。它可以在一个线程中运行,并可以通过 Future 接口的方法来获取结果。
3. 异步任务
使用 CompletableFuture 可以在后台线程中异步执行任务,而不会阻塞主线程。示例中使用 runAsync() 和 supplyAsync() 方法分别执行没有返回值和有返回值的任务。
4. 任务依赖
CompletableFuture 允许通过链式调用来构建复杂的任务依赖关系。常用的方法有:
thenRun(): 在前一个任务完成后执行另一个任务,不接受参数且没有返回值。
thenAccept(): 在前一个任务完成后消费结果,接受一个参数但没有返回值。
thenApply(): 在前一个任务完成后处理结果,接受一个参数并返回一个新的 CompletableFuture。
5. 并行任务
anyOf(): 允许多个任务并行执行,并返回第一个完成的任务的结果。
allOf(): 允许多个任务并行执行,等所有任务完成后聚合结果。
6. 回调
CompletableFuture 支持在某个任务完成后执行回调方法。常用的方法有:
whenComplete(): 执行回调任务,没有返回值。
handle(): 执行回调任务并返回结果,可以处理异常情况。
6. 业务的动态部署
总结:第一次面试,八股背的深度不够,好多细节遗漏接着面,接着背
全部评论
相关推荐

点赞 评论 收藏
分享