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. 业务的动态部署

总结:第一次面试,八股背的深度不够,好多细节遗漏接着面,接着背
全部评论

相关推荐

09-16 19:41
门头沟学院 Java
依旧只写记得的部分一面:1.自我介绍+介绍个人博客2.看我个人博客,让我讲讲我写的博客的东西(有关线程池源码的,参考性并不是很大)2.5.在讲博客内容时穿插多线程八股3.看我多线程这块好像挺好的,手撕奖励一道多线程打印,一个打印A,一个打印B(还好上次回去恶补了一下)4.问实习相关,主要是自己做了啥事情,整个系统大概啥样,回答思路是首先大致介绍系统作用,然后聚焦个人做的模块,再补充上下游把全链路讲清楚大概这么多,一共45-50min,出来等了5min告知通过准备第二轮二面:1.看我项目用了限流的东西问了一下我对限流算法有无了解,回答了令牌桶和时间窗口2.初现端倪,开始问我TCP有无限流概念的体现(主播简历一点408计网没写),回答流量控制和拥塞控制3.接2,给了一个例子,比如通过TCP连接下载数据的时候的场景,让我画TCP数据传输速率时间图(v-t图)。直接给我干懵逼了,他说你们应该学过吧,我说我完全没听过,他说没事,你学过的知识里应该有体现(体现在哪),于是开始画图。这题我主要的考虑是作为发送方来发数据的速率,因为流量控制和拥塞控制实际上都是控制发送方的窗口大小,其中拥塞控制是发送方根据网络拥堵程度控制自己的窗口大小。所以主播在这里考虑拥塞避免的一些算法(慢开始、拥塞避免、快重传、快恢复),通过窗口变化来推算传输速率的变化。所以首先画了一个拥塞窗口随时间变化的图,然后根据这个图画出最终他要的v-t图。他当时没说对不对,下来之后我用claude给我模拟了一下,思路基本没啥问题,最后的v-t图实际上跟窗口变化是基本差不多的。4.继续问计网,让我讲讲TCP三次握手和四次挥手,主播已经不记得那些状态具体叫啥了,索性说那我继续画图吧,然后就直接开画,画的中间穿插一些细节问题,比如第二次挥手跟第三次挥手之间可能是谁给谁传数据(服务端给客户端传)之类的细节。5.画完开始问一些我觉得自己擅长的,我说线程池,然后开始吟唱一些普通线程池八股6.追问你看过一些Web容器的源码比如tomcat吗?回答没看过,说没事那你觉得像这种Web服务的容器用传统线程池合适吗?回答思路是不太合适,我是从用户响应这块考虑的,假设用传统线程池,那么请求只来了一部分,就到达核心线程数,只有等任务队列满之后才会继续创临时线程来处理,这样如果请求多一点的话,请求的平均响应时间可能就会慢,我的想法就是优先创建线程,实在没线程可创了再放进队列里可能会好一些。下来查发现tomcat也确实是这样设计。7.然后继续问我看过啥源码吗,我依然回答线程池。。。大哥看我这么喜欢线程池奖励了一道设计题,让我设计一个工业可用的线程池,我的大概思路就是1.线程池集中管理、2.线程池配置热更新(分成本地更新和远程更新,本地可以考虑WatchService,远程比如放配置中心Nacos这种,Nacos的SDK里有ConfigService可以注册监听器监听配置发布)、3.线程池监控、4.线程池告警8.然后说是给我一个拔高的问题,问我线程池中死锁如何产生的,这个一时没想出来,依然从死锁的四个必要条件开始推,1.互斥、2.请求保持、3.不可剥夺、4.循环等待,推断出应该是由于出现循环等待导致四个条件同时满足(死锁只有在四个条件同时成立时可能出现,注意是可能),但是推了一会依然没推出来,让下来再想想。9.剩下就问了一个Spring有哪些拓展点,BeanPostProcessor、InitializingBean、还有Aware系列比如AppilicationContextAware这种,当时只记得这三个了。大概就这些,貌似也30-40min左右,哥们看手机显示您今天的面试已结束,还以为凉透了,刚准备去抽奖然后hr喊我名字说我通过了,前脚刚迈出来后脚就过了属于是,有点懵逼,然后抽奖走人,抽了个水杯正好拿来泡茶。总结就是真的蛮看运气的,周末两天复习实际上他问我的东西一点没看,上周刚被拷打过JVM于是恶补了一下,结果两面都基本在问线程池和408,所以感觉还是平常心看待吧,面试官想问啥确实不是面试者能左右的。。。主播以面一次赚一次的态度来的,降低预期,反而没这么紧张了,反正还是那句话,找工作就是找屎,别让一坨屎影响生活吧,虽然该找还得找。。。而且线下面的好处就是流程快和好沟通,一言不合直接画图给面试官看感觉确实比线上用手比划爽多了。发出来让大火了解一下线下面大概是个啥强度,同时希望后续顺利一些吧。。
查看14道真题和解析
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务