腾讯云最新社招面经出炉(面试原题+答案解析)

本文详细介绍了在Java技术面试中常见的知识点,包括零拷贝的概念及其优化技术,IO模型的种类,NIO与多路复用的区别,ReentrantLock的原理,AQS(AbstractQueuedSynchronizer)的使用,乐观锁与悲观锁的实现,Paxos协议的工作流程,B+树的特点以及TCP的拥塞控制机制。此外,还涵盖了数据库分库分表的缺点和分布式事务的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

大家好,今天给大家分享一篇腾讯云的面经,以下是面试题和答案。加油,一起卷。

  1. 聊聊项目,好的设计,好的代码

  2. 谈谈什么是零拷贝?

  3. 一共有几种 IO 模型?NIO 和多路复用的区别?

  4. Future 实现阻塞等待获取结果的原理?

  5. ReentrantLock和 Synchronized 的区别?Synchronized 的原理?

  6. 聊聊AOS?ReentrantLock的实现原理?

  7. 乐观锁和悲观锁, 让你来写你怎么实现?

  8. Paxos 协议了解?工作流程是怎么样的?

  9. B+树聊一下?B+树是不是有序?B+树和B-树的主要区别?

  10. TCP的拥塞机制

  11. 工作中有过JVM实践嘛

  12. 数据库分库分表的缺点是啥?

  13. 分布式事务如何解决?TCC 了解?

  14. RocketMQ 如何保证消息的准确性和安全性?

  15. 算法题:三个数求和

1.聊聊项目,好的设计,好的代码

项目的话,你可以聊聊你平时做的项目,尤其有亮点的项目。如果没有什么特别亮点的项目,也可以说说一些好的设计,或者你优化了什么接口,性能提升了多少,优化了什么慢SQL都可以。甚至是一些好的代码写法都可以。

2. 谈谈什么是零拷贝?

零拷贝是指计算机执行IO操作时,CPU不需要将数据从一个存储区域复制到另一个存储区域,从而可以减少上下文切换以及CPU的拷贝时间。它是一种I/O操作优化技术。

传统 IO 的执行流程

传统的IO流程,包括read和write的过程。

  • read:把数据从磁盘读取到内核缓冲区,再拷贝到用户缓冲区

  • write:先把数据写入到socket缓冲区,最后写入网卡设备。

  1. 用户应用进程调用read函数,向操作系统发起IO调用,上下文从用户态转为内核态(切换1)

  2. DMA控制器把数据从磁盘中,读取到内核缓冲区。

  3. CPU把内核缓冲区数据,拷贝到用户应用缓冲区,上下文从内核态转为用户态(切换2),read函数返回

  4. 用户应用进程通过write函数,发起IO调用,上下文从用户态转为内核态(切换3)

  5. CPU将用户缓冲区中的数据,拷贝到socket缓冲区

  6. DMA控制器把数据从socket缓冲区,拷贝到网卡设备,上下文从内核态切换回用户态(切换4),write函数返回

传统IO的读写流程,包括了4次上下文切换(4次用户态和内核态的切换),4次数据拷贝(两次CPU拷贝以及两次的DMA拷贝)。

零拷贝实现方式

零拷贝并不是没有拷贝数据,而是减少用户态/内核态的切换次数以及CPU拷贝的次数。零拷贝一般有这三种实现方式:

  • mmap+write

  • sendfile

  • 带有DMA收集拷贝功能的sendfile

mmap+write

mmap就是用了虚拟内存这个特点,它将内核中的读缓冲区与用户空间的缓冲区进行映射,以减少数据拷贝次数!

  1. 用户进程通过mmap方法向操作系统内核发起IO调用,上下文从用户态切换为内核态。

  2. CPU利用DMA控制器,把数据从硬盘中拷贝到内核缓冲区。

  3. 上下文从内核态切换回用户态,mmap方法返回。

  4. 用户进程通过write方法向操作系统内核发起IO调用,上下文从用户态切换为内核态。

  5. CPU将内核缓冲区的数据拷贝到的socket缓冲区。

  6. CPU利用DMA控制器,把数据从socket缓冲区拷贝到网卡,上下文从内核态切换回用户态,write调用返回。

mmap+write实现的零拷贝,I/O发生了4次用户空间与内核空间的上下文切换,以及3次数据拷贝(包括了2次DMA拷贝和1次CPU拷贝)。

sendfile

sendfile表示在两个文件描述符之间传输数据,它是在操作系统内核中操作的,避免了数据从内核缓冲区和用户缓冲区之间的拷贝操作

  1. 用户进程发起sendfile系统调用,上下文(切换1)从用户态转向内核态

  2. DMA控制器,把数据从硬盘中拷贝到内核缓冲区。

  3. CPU将读缓冲区中数据拷贝到socket缓冲区

  4. DMA控制器,异步把数据从socket缓冲区拷贝到网卡,

  5. 上下文(切换2)从内核态切换回用户态,sendfile调用返回。

sendfile实现的零拷贝,I/O发生了2次用户空间与内核空间的上下文切换,以及3次数据拷贝。其中3次数据拷贝中,包括了2次DMA拷贝和1次CPU拷贝。

带有DMA收集拷贝功能的sendfile

linux 2.4版本之后,对sendfile做了优化升级,引入SG-DMA技术,其实就是对DMA拷贝加入了scatter/gather操作,它可以直接从内核空间缓冲区中将数据读取到网卡。使用这个特点搞零拷贝,即还可以多省去一次CPU拷贝。

  1. 用户进程发起sendfile系统调用,上下文(切换1)从用户态转向内核态

  2. DMA控制器,把数据从硬盘中拷贝到内核缓冲区。

  3. CPU把内核缓冲区中的文件描述符信息(包括内核缓冲区的内存地址和偏移量)发送到socket缓冲区

  4. DMA控制器根据文件描述符信息,直接把数据从内核缓冲区拷贝到网卡

  5. 上下文(切换2)从内核态切换回用户态,sendfile调用返回。

可以发现,sendfile+DMA scatter/gather实现的零拷贝,I/O发生了2次用户空间与内核空间的上下文切换,以及2次数据拷贝。其中2次数据拷贝都是包DMA拷贝。这就是真正的 零拷贝(Zero-copy) 技术,全程都没有通过CPU来搬运数据,所有的数据都是通过DMA来进行传输的。

看一遍就理解:零拷贝详解

3. 一共有几种 IO 模型?NIO 和多路复用的区别?

一共有五种IO模型

  • 阻塞IO模型

  • 非阻塞IO模型

  • IO多路复用模型

  • IO模型之信号驱动模型

  • IO 模型之异步IO(AIO)

NIO(非阻塞IO模型)

NIO,即Non-Blocking IO,是非阻塞IO模型。非阻塞IO的流程如下:

  1. 应用进程向操作系统内核,发起recvfrom读取数据。

  2. 操作系统内核数据没有准备好,立即返回EWOULDBLOCK错误码。

  3. 应用程序进程轮询调用,继续向操作系统内核发起recvfrom读取数据。

  4. 操作系统内核数据准备好了,从内核缓冲区拷贝到用户空间。

  5. 完成调用,返回成功提示。

NIO(非阻塞IO模型)存在性能问题,即频繁的轮询,导致频繁的系统调用,同样会消耗大量的CPU资源。可以考虑IO复用模型去解决这个问题。

IO多路复用模型

IO多路复用就是,等到内核数据准备好了,主动通知应用进程再去进行系统调用。

IO复用模型核心思路:系统给我们提供一类函数(如我们耳濡目染的select、poll、epoll函数),它们可以同时监控多个fd的操作,任何一个返回内核数据就绪,应用进程再发起recvfrom系统调用。

IO多路复用之select

应用进程通过调用select函数,可以同时监控多个fd,在select函数监控的fd中,只要有任何一个数据状态准备就绪了,select函数就会返回可读状态,这时应用进程再发起recvfrom请求去读取数据。

非阻塞IO模型(NIO)中,需要N(N>=1)次轮询系统调用,然而借助select的IO多路复用模型,只需要发起一次询问就够了,大大优化了性能。

但是呢,select有几个缺点:

  • 监听的IO最大连接数有限,在Linux系统上一般为1024。

  • select函数返回后,是通过遍历fdset,找到就绪的描述符fd。(仅知道有I/O事件发生,却不知是哪几个流,所以遍历所有流)

因为存在连接数限制,所以后来又提出了poll。与select相比,poll解决了连接数限制问题。但是呢,select和poll一样,还是需要通过遍历文件描述符来获取已经就绪的socket。如果同时连接的大量客户端,在一时刻可能只有极少处于就绪状态,伴随着监视的描述符数量的增长,效率也会线性下降。

IO多路复用之epoll

为了解决select/poll存在的问题,多路复用模型epoll诞生,它采用事件驱动来实现,流程图如下:

epoll先通过epoll_ctl()来注册一个fd(文件描述符),一旦基于某个fd就绪时,内核会采用回调机制,迅速激活这个fd,当进程调用epoll_wait()时便得到通知。这里去掉了遍历文件描述符的坑爹操作,而是采用监听事件回调的机制。这就是epoll的亮点。

4. Future 实现阻塞等待获取结果的原理?

Future.get()用于异步结果的获取。它是阻塞的,背后原理是什么呢?

我们可以看下FutureTask的类结构图:

FutureTask实现了RunnableFuture接口,RunnableFuture继承了Runnable和Future这两个接口, 对于Runnable,我们太熟悉了, 那么Future呢?

Future 表示一个任务的生命周期,并提供了相应的方法来判断是否已经完成或取消,以及获取任务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值