1. 开场寒暄
- 面试官与我进行了初步的寒暄,了解对未来生涯的规划和心态。
2. 算法题目考察
解题思路:
- 单链表按照每k个元素进行翻转。
- 候选人提出了用从后往前递归+记录层数以翻转链表,记录前置节点和后置节点以将翻转完成的链表重新接回原链表处。
答题结果:
- 思路正确但实现能力差,没处理好ACM模式下自行解决链表输入及创建的问题。
3. 思维题目考察
8个小球,其中7个质量相同,1个质量小,现在给你一个称,最少称几次可以确保一定能找到不同的小球。
答:分为3 3 2。
- 先称33,第一次
-
- 33等重,称2,第二次,得出结果。
- 33不等重,取轻的3,选其中2个称,第二次
-
-
- 2个等重,剩余一个是
- 2个不等重,轻的是
-
最终答案是2次。
4. 计算机相关知识考察
Q1:计算机网络中的TCP协议有哪些缺点?
参考深度思考|TCP协议存在那些缺陷?回答:
TCP协议是一种面向连接的、可靠的、基于字节流的传输层通信协议,广泛应用于互联网。然而,它也存在一些缺点:
1. 滑动窗口机制导致的性能问题
- 停-等策略:TCP的滑动窗口机制本质上是一种“停-等”策略。发送方需要等待接收方确认已接收的数据后,才能继续发送新的数据。这种机制会导致数据传输的延迟,尤其是在网络状况不佳或带宽较低的情况下,数据传输速度会显著下降。
- 队头阻塞(HOL Blocking):当队列的第一个数据包受阻时,整个队列的数据包都会受到影响。例如,在缓存式输入的交换机中,如果第一个数据包因为网络拥塞或其他原因无法及时传输,后续的数据包也会被阻塞,从而降低整个网络的传输效率。
- 接收窗口限制:TCP的滑动窗口大小取决于接收方的缓冲区大小。如果接收方的缓冲区较小,发送方的数据传输速度就会受到限制,即使网络带宽足够,也无法充分利用带宽。
2. 拥塞控制问题
- 拥塞窗口(cwnd)的限制:TCP的拥塞控制机制通过拥塞窗口(cwnd)来限制发送方的发送速率。然而,cwnd的大小受到网络拥塞情况的影响。如果网络拥塞严重,cwnd会迅速减小,导致发送方的发送速率大幅下降。即使网络拥塞情况缓解,cwnd也需要一定时间才能恢复到正常水平,这会影响数据传输的效率。
- 拥塞控制算法的局限性:TCP的拥塞控制算法(如TCP Reno、TCP Cubic等)在某些情况下可能无法准确判断网络的拥塞状态。例如,当网络延迟较高时,TCP可能会误判网络拥塞,从而过度减小cwnd,导致吞吐率下降。
3. 连接建立和关闭的开销
- 三次握手:TCP建立连接需要经过三次握手过程,这增加了连接建立的时间开销。在高延迟的网络环境中,三次握手的时间可能会显著增加,尤其是在需要频繁建立和关闭连接的应用场景中,这种开销会更加明显。
- 四次挥手:TCP关闭连接需要经过四次挥手过程,同样会增加时间开销。此外,在某些情况下,如果一方在关闭连接时没有正确处理,可能会导致连接无法正常关闭,从而占用系统资源。
4. 可靠性与效率的平衡问题
- 冗余机制:TCP为了保证数据的可靠性,采用了序列号、确认应答(ACK)、重传等机制。这些机制虽然能够确保数据的完整性和可靠性,但也增加了协议的复杂性和传输的开销。例如,当数据包丢失时,TCP需要等待超时重传,这会导致数据传输的延迟。
- 不适合实时应用:由于TCP的可靠性和拥塞控制机制,它在某些实时应用(如视频会议、在线游戏等)中表现不佳。这些应用对数据传输的实时性要求较高,而TCP的重传机制和拥塞控制会导致数据传输的延迟,影响用户体验。
5. 对网络变化的适应性问题
- 网络拓扑变化:TCP对网络拓扑变化的适应性较差。当网络拓扑发生变化(如链路故障、路由变化等)时,TCP需要重新调整拥塞窗口和滑动窗口,这会导致数据传输的中断或延迟。
- 多路径传输:TCP不支持多路径传输。在某些情况下,网络中可能存在多条路径可供选择,但TCP只能选择一条路径进行数据传输。如果选择的路径出现拥塞或故障,TCP无法自动切换到其他路径,从而影响数据传输的效率。
6. 安全性问题
- 中间人攻击:TCP协议本身没有提供加密机制,因此容易受到中间人攻击。攻击者可以截获、篡改或伪造TCP数据包,从而获取敏感信息或干扰正常的通信。
- SYN洪水攻击:TCP的三次握手过程容易受到SYN洪水攻击。攻击者可以通过发送大量的SYN请求来消耗服务器的资源,导致服务器无法正常处理合法的连接请求。
总结
TCP协议虽然在可靠性方面表现出色,但在性能、实时性、适应性和安全性等方面存在一些缺点。这些缺点在某些应用场景中可能会限制其使用效果,因此在选择网络协议时,需要根据具体的应用需求和网络环境进行权衡。
Q2:TCP协议为确保可靠性传输采取的手段:
TCP连接确保可靠性方法如下:
1. 数据块大小控制:
应用数据被分割成TCP认为最合适发送的数据块,再传输给网络层,数据块被称为报文段或段。
2. 序列号:
TCP给每个数据包指定序列号,接收方根据序列号对数据包进行排序,并根据序列号对数据包去重。
3. 校验和:
TCP将保持它首部和数据的校验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到报文的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。
4. 流量控制:
TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP利用滑动窗口实现流量控制。
5. 拥塞控制:
当网络拥塞时,减少数据的发送。
6. 确认应答:
通过ARQ协议实现。基本原理是每发完一个分组就停止发送,等待对方确认。如果没收到确认,会重发数据包,直到确认后再发下一个分组。
Q2.5:追问:TCP的3次握手解决的是什么问题?
参考TCP三次握手的设计思想——八股文不会告诉你的那些事回答:
TCP通过三次握手解决的问题是:
在不可靠的信道上,通信双方需要达成一致以建立可靠的数据传输。因此三次握手不是TCP本身的要求,而是在不可靠的信道上可靠地传输信息这一需求所导致的。
而在此前提下,三次握手,理论上是建立可靠全双工连接(指通信双方可以在同一时刻进行双向数据传输)的最小和最佳的解决方案。
三次握手:
- 第一次握手Client -> Server:使Server确认Client的发送信道可靠,Server也确认自己的接收信道可靠。
- 第二次握手Server -> Client:Client确认双方的发送信道和接收信道都可靠。
- 第三次握手Client -> Server:Server进一步确认Client的接收信道可靠,也确认自己的发送信道可靠,因此确认双方的发送信道和接收信道都可靠。
最终,Server和Client双方都确认,自己和对方的发送和接收通道已经可以正常运行。
两次握手的问题:
- 理论分析层面:Server无法确认Client的接收信道和自己的发送信道是否可靠。
- 具体实践层面,Server不知道客户端是否收到了 SYN-ACK 响应,这可能导致半开连接或超时重传问题。
四次握手的问题:
带来不必要的开销,且无法额外提高可靠性,因此不必多此一举。
5. Java相关知识考察
Q1:Synchronized和Lock的底层实现原理有什么区别:
synchronized和Lock的底层实现差异如何导致适用场景的分化
要理解synchronized和Lock的区别,需抓住它们的设计哲学和实现路径差异,最终推导出各自适用场景。
一、底层原理的核心差异
1. synchronized:依赖JVM的“自动化锁”
- 实现路径:
依赖JVM的Monitor内置锁机制,通过对象头的Mark Word
(记录锁状态)和锁升级(偏向→轻量→重量级锁)实现。
-
- Monitor本质:一个与对象绑定的互斥锁(操作系统Mutex的封装)。
- 锁升级逻辑:JVM根据竞争激烈程度动态调整锁状态:
- 关键限制:
synchronized的灵活性较低,只能用于方法或代码块。此外,锁的获取和释放完全由JVM控制,虽然可靠性高,但是功能灵活性受限:
-
- 无法实现公平锁(默认非公平)
- 无法中断等待锁的线程
- 无法设置超时时间
2. Lock(以ReentrantLock为例):Java层的“手动控制锁”
- 实现路径:
是Java提供的显式锁机制:lock 通常是 java.util.concurrent.locks 包下的接口,如 ReentrantLock 是其实现类之一,以ReentrantLock为例: - 基于AQS(AbstractQueuedSynchronizer)框架,与synchronized最大的区别是:
-
- 显式控制:开发者必须手动调用
lock()
和unlock()
- 手动更新锁状态:通过
Unsafe.compareAndSwap
原子更新锁状态 - 主动维护队列:将未获取锁的线程封装并添入队列
- 显式控制:开发者必须手动调用
- 核心优势:
通过手动控制锁状态和队列,实现灵活的功能扩展:
-
- 支持公平/非公平策略(构造函数指定)
- 可中断锁获取(
lockInterruptibly()
) - 超时尝试(
tryLock(timeout)
) - 多条件变量(
newCondition()
)
Q1.5:追问:Synchronized和Lock的底层实现原理决定了他们的使用场景有哪些不同?
从底层差异到场景分化的逻辑推导
1. 为什么synchronized适合简单场景?
- JVM自动优化:
锁升级机制让它在低竞争场景下性能极佳。例如,单线程访问时,偏向锁直接跳过同步;轻度竞争时,自旋锁减少线程切换。 - 功能局限性:
由于JVM自动管理锁状态,无法实现精细控制(如超时、中断)。若强行用synchronized实现复杂需求,需额外代码(如轮询检查),反而降低性能。
典型场景:
- 单例模式的双重检查锁
- 简单的线程安全计数器
- 无复杂协作的同步代码块
2. 为什么Lock适合高并发复杂场景?
- 手动控制与AQS队列:
-
- 减少内核态切换:在高竞争时,AQS通过CAS(更新锁状态的原子操作)和队列管理避免直接进入重量级锁状态(synchronized此时会触发内核阻塞),减少上下文切换。
- 功能扩展性:
-
-
- 通过
tryLock
实现“尝试获取锁-失败后执行其他逻辑”(避免死锁) - 通过
Condition
实现精细化线程协作(如生产者-消费者模型中的空/满队列分离)
- 通过
-
典型场景:
- 高并发的线程池任务调度
- 需要超时控制的分布式锁模拟
- 复杂条件判断的线程协作(如阻塞队列)
6. 开放型问题
Q:能不能跟我描述一个你曾经遇到过的技术难题,你又是如何分析并解决这个问题的?
非常建议提前准备一个根据岗位定制的答案。
7. 反问及解答环节
- 候选人提问:这次面试是按实习基地来面还是按暑期实习来面?
- 面试官回答:按暑期实习来面,主要目的是招聘本科毕业后直接工作的同学,提前进行双向选择,希望双方能多一些了解和双向选择的机会。
8. 反思总结
- 理论知识不够扎实(如数据结构、算法、计算机网络等),需进一步加强,尤其是深入到具体的应用场景,从而理解透彻和内化于心。
- 动手能力需提高,多参与实际项目,增强快速上手的能力。代码的实现能力也是其中一部分,本次面试因为对ACM模式的掌握不足,没能处理好输入和构建数据结构的问题,导致解题失败。
- 强调了不同阶段(如实习和秋招)的考察重点不同,实习更注重基础理论和动手能力,而秋招会更看重实际经验和综合能力(尤其是实习经验)。