综合81-100
81.为什么 TCP 挥手需要有 TIME_WAIT 状态?
展开
- 可靠性:TIME_WAIT 出现在主动关闭方,等待 2×MSL 的目的是若对端最后一个
FIN
的确认ACK
丢失,可以在对端重传FIN
时再次发送ACK
,确保四次挥手可靠结束。 - 安全性(避免旧报文干扰):等待 2×MSL 能让网络中属于该连接四元组的“旧的/重复”分段全部过期,避免同一四元组被新连接复用时收到幽灵数据导致数据错乱。
- 对象:只有主动关闭连接的一侧进入 TIME_WAIT。
- 时长:2×MSL(实现通常设置为 1~4 分钟)。TIME_WAIT 激增会消耗端口/内核资源,可通过长连接/连接复用、增大临时端口范围、合理服务端并发设计等缓解(不应简单绕过该语义)。
82.Spring Boot 的核心特性有哪些?
展开 中等 VIP Spring Boot 后端
- 自动配置(AutoConfiguration):基于类路径与条件注解按需装配常用组件,减少样板配置。
- Starters:按功能划分的起步依赖,开箱即用(如
spring-boot-starter-web
、data-redis
)。 - 嵌入式容器:内置 Tomcat/Jetty/Undertow,Jar 即可运行,便于部署与容器化。
- 外部化配置:统一的
application.yml
/properties
,@ConfigurationProperties
,Profile
多环境支持。 - 约定优于配置:合理默认值提升开发效率,可逐步显式化配置。
- 开发者体验:Spring Initializr 脚手架,良好测试支持(
@SpringBootTest
)。
83.为什么 HashMap 在 Java 中扩容时采用 2 的 n 次方倍?
展开 中等 Java 集合 Java
-
高效寻址,算索引下标的时候用与位运算:容量为 2 的幂,索引可用
(n - 1) & hash
,避免昂贵的取模运算,与位运算配合能更好地利用哈希高位,降低冲突概率(JDK 8 混淆高位至低位)。 -
高效再分布:扩容 到
newCap = oldCap << 1
时,节点要么保持原索引,要么移动到oldIndex + oldCap
,无需重新计算完整哈希,迁移成本低。 -
决策规则:看新增位(
hash & oldCap
)。等于 0 → 保持oldIndex
;不等于 0 → 移动到oldIndex + oldCap
。 -
公式:
oldIndex = hash & (oldCap - 1)
;newIndex = ((hash & oldCap) == 0) ? oldIndex : oldIndex + oldCap
。
int oldIndex = hash & (oldCap - 1);
int newIndex = (hash & oldCap) == 0 ? oldIndex : oldIndex + oldCap;
84.你在项目中使用的 Redis 客户端是什么?
展开 简单 VIP 后端 Redis
-
我们项目里用默认的Lettuce,加上Redisson做分布式锁
-
Java 生态常见选择:
- Lettuce:Netty 驱动,基于异步/响应式,线程安全,Spring Data Redis 默认推荐。
- Jedis:历史较久,直观易用;早期连接非线程安全,需使用连接池。
- Redisson:提供分布式对象与高级特性(锁、限流、队列、延时队列、执行器等)。
-
选型建议:
- 常规 KV/缓存:优先 Lettuce(性能/线程模型优)。
- 需要高级分布式原语:考虑 Redisson。
85.TCP 超时重传机制是为了解决什么问题?
展开 简单 VIP 网络
- 目的:确保TCP的可靠性
- 发送方每发送一个报文段,都会启动一个计时器,然后等待ACK,如果计时器超时,则重传报文段
- 过程
- 发送数据并启动计时器:发送方每发送一个包含数据的TCP报文段,都会为其启动一个独立的重传计时器。
- 等待确认:发送方等待接收方返回对该报文段的确认(ACK)。
- 超时处理:
- 成功接收ACK:如果在计时器超时之前收到了有效的ACK,则取消该计时器,数据传输成功。
- 计时器超时:如果在规定时间内未能收到ACK,则认为数据包丢失。发送方会重新 发送该数据包,并重新设置一个更长的超时时间(通常是上一次的两倍,即“指数退避”策略),然后再次等待确认。
- 多次重传失败:如果多次重传仍然失败,发送方会认为网络连接存在严重问题,最终可能会放弃连接。
86.简述 MyBatis 的插件运行原理,以及如何编写一个插件?
展开 中等 VIP 后端 MyBatis
- 原理
基于动态代理与责任链模式,对核心组件方法进行拦截。
-
编写步骤
- 实现
org.apache.ibatis.plugin.Interceptor
接口,重写三个方法:- Object intercept(Invocation invocation):核心拦截逻辑,这里编写自定义行为。
- Object plugin(Object target):决定是否包装目标对象,通常使用 Plugin.wrap(target, this) 创建代理。
- void setProperties(Properties properties):可选,用于接收插件配置参数。
- 使用注解指定拦截点:
@Intercepts
:标注类,包含一个或多个@Signature
。@Signature
:指定拦截的类型(type)、方法(method)和参数类型(args)。- type:
- Executor(执行器,负责整体 SQL 执行)。
- ParameterHandler(参数处理器,处理 SQL 参数)。
- ResultSetHandler(结果集处理器,处理查询结果)。
- StatementHandler(语句处理器,处理 JDBC Statement)。
- type:
- 配置插件:
- 在 mybatis-config.xml 中添加
标签,指定插件类和可选属性。 - 或者在spring config配置
- 在 mybatis-config.xml 中添加
- 实现
-
四个核心接口及其方法的执行顺序
-
典型查询调用栈(简化):
- Executor.query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
- 作用:
- 先查本地缓存/二级缓存(若开启)。
- 未命中时创建 CacheKey 与 BoundSql,委托 BaseExecutor.queryFromDatabase → doQuery。
- 参数:
- ms:映射的语句与配置(SQL、参数映射、返回映射等)。
- parameter:入参对象(可为 Map/POJO/基础类型)。
- rowBounds:逻辑分页(offset/limit)。
- resultHandler:自定义结果处理器(可选)。
- 作用:
- StatementHandler.prepare(Connection, Integer timeout) → parameterize(Statement)
- 创建并配置 JDBC Statement/PreparedStatement,绑定 SQL。
- StatementHandler.query(Statement, ResultHandler)
- 内部委托 ParameterHandler.setParameters(PreparedStatement) 设置占位符参数,执行 JDBC 查询。
- ResultSetHandler.handleResultSets(Statement)
- 将 ResultSet 映射为目标对象集合(映射列到属性、嵌套映射、延迟加载等)。
- Executor 写入本地/二级缓存(若可缓存),返回结果集合。
- Executor.query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
-
核心接口职责速览:
- Executor:增删改
update(...)
,查询query(...)
,commit/rollback
,createCacheKey
,clearLocalCache
,装饰实现如 CachingExecutor 负责缓存拦截。 - StatementHandler:
prepare/parameterize/batch/update/query
,封装 JDBC Statement 的创建、参数化与执行。 - ParameterHandler:
getParameterObject/setParameters
,负责将实参按映射设入 PreparedStatement。 - ResultSetHandler:
handleResultSets/handleOutputParameters
,负责将结果集映射为对象或集合。
- Executor:增删改
-
-
案例
- 记录SQL的执行耗时
- 拦截点:StatementHandler的prepare方法
- 避免误操作导致的UPDATE/DELETE 全表
- 拦截点:Executor的update方法,判断sql的类型
- 记录SQL的执行耗时
-
示例:
@Intercepts({
@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "update",
args = {MappedStatement.class, Object.class})
})
public class MetricInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.nanoTime();
try { return invocation.proceed(); }
finally { long costMicros = (System.nanoTime() - start) / 1000; /* 记录耗时 */ }
}
@Override
public Object plugin(Object target) { return Plugin.wrap(target, this); }
@Override
public void setProperties(Properties properties) { }
}
87.数组和链表在 Java 中的区别是什么?
展开 中等 Java 集合 Java
- 存储结构:数组连续内存,链表离散 节点(额外指针/对象头开销)。
- 访问性能:数组随机访问 O(1);链表需要遍历 O(n)。
- 插入/删除:
- 数组中间插入/删除需搬移 O(n);
- 链表结点插入/删除 O(1)(已得前驱),否则是O(n)。
- 修改元素
- 数组:通过索引直接修改,时间复杂度为 O(1)。
- 链表:需要遍历到目标节点,时间复杂度为 O(n)。
- 大小调整:数组成本高,链表更加灵活
- 典型实现:
ArrayList
基于数组,LinkedList
为双向链表。 - 使用建议:读多随机访问选数组;频繁中间插入/删除且能定位前驱选链表。
88.Redis 中常见的数据类型有哪些?
展开 简单 VIP 后端 Redis
- String、Hash、List、Set、Sorted Set(ZSet)
- Bitmap、Bitfield、HyperLogLog、Geo、Stream
89.TCP 滑动窗口的作用是什么?
展开 中等 VIP 网络
- 滑动窗口会根据接收方的缓冲区剩余空间大小(Window Size)来限制发送方一次能发的未确认数据量
- 更好的流量控制,以动态的控制窗口可大小
- 增大了吞吐量,允许发送方连续发多个数据段
90.介绍一下 Reactor 线程模型?
展开 中等 VIP Netty 后端
- 单 Reactor 单线程:一个线程完成事件多路复用、接受连接、读写与业务处理,简单但扩展性差。
- 单 Reactor 多线程:Reactor 负责 accept 与事件分发,工作线程池处理业务逻辑,提高并发度。
- 多 Reactor 多线程(主从):主 Reactor 专职 accept,多个从 Reactor 负责读写与分发到工作线程池,常见于高并发框架。
- Netty 对应:
bossGroup
(accept)+workerGroup
(I/O 读写与处理),基于 epoll/kqueue/select 的事件驱动。
91.Java 线程池核心线程数在运行过程中能修改吗? 如何修改?
展开
能改。通过 ThreadPoolExecutor
动态调整:
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
executor.setCorePoolSize(20);
executor.setMaximumPoolSize(100);
executor.allowCoreThreadTimeOut(true); // 可选:允许核心线程超时回收
在 Spring 中可用 ThreadPoolTaskExecutor
在启动前配置;运行期需获取底层 ThreadPoolExecutor
再调整。
92.TCP/IP 四层模型是什么?
展开 简单 VIP 网络
- 应用层(Application):HTTP、DNS、FTP、SMTP、SSH 等
- 传输层(Transport):TCP、UDP
- 网络层(Network):IP、ICMP、IGMP、NAT 等
- 网络接口层(Link / Network Interface):以太网、PPP 等
93.说说 MyBatis 的缓存机制?
展开
- 一级缓存,SqlSession级别,默认开启
- 每一个SqlSession会对应创建一个执行器,执行器里会有一个存储键值对的对象,key是根据查询语句和参数确定的
- 一级缓存在应用层规避了读已提交隔离级别下的不可重复读问题,在一个SqlSession里读不到其他事务已提交的数 据
- 二级缓存是mapper级别,默认关闭,可以自定义实现,实现Cache接口或者用集成包
94.什么是 Spring Boot?
展开 中等 VIP Spring Boot 后端
- 对 Spring 进行“约定优于配置”的封装,提供自动配置、起步依赖、嵌入式容器与外部化配置,显著降低样板代码与环境搭建成本。
- 支持可观测性与生产特性(Actuator、健康检查、指标、日志),便于云原生与容器化部署。
- 与传统 Spring 对比:无需繁琐 XML/JavaConfig 手动装配,专注业务开发。
95.Java 中如何创建多线程?
展开
- 继承
Thread
:重写run()
,new Thread().start()
。 - 实现
Runnable
:new Thread(new Runnable(){...}).start()
或 lambda。 - 实现
Callable<V>
:配合FutureTask
或ExecutorService.submit
获取返回值与异常。 - 线程池:
ExecutorService
(newFixedThreadPool
、newCachedThreadPool
、ThreadPoolExecutor
自定义)。 - 异步工具:
CompletableFuture
组合式异步编程,ForkJoinPool
分治任务。
96.Redis 中跳表的实现原理是什么?
展开 困难 VIP 后端 Redis
- 跳表是多层有序链表,通过随机层高实现对数期望复杂度的查找/插入/删除(平均 O(log n))。
- 节点包含多个前进指针,查找自顶层向下逐层推进,最终在最低层定位目标。
- ZSet 底层通常由“字典(member->score)+ 跳表(按 score 有序)”组成;小集合使用压缩结构以节省内存。
- 插入时通过随机函数生成层高(常用 p=0.25),维护跨度用于排名/区间查询;支持按分值与按字典序范围查询。
97.Redis 性能瓶颈时如何处理?
展开 中等 VIP 后端 Redis
- 模型与数据:避免大 Key/大 Value,拆分热 Key,控制集合过大;使用合适数据结构与过期策略。
- I/O 与协议:Pipeline 批处理、合理批量大小;减少频繁往返;用 Lua 保证原子性并减少网络开销。
- 持久化与内存:合适的 RDB/AOF 策略,规避高峰期 fork;关注内存碎片率与 eviction 策略;合理
maxmemory
。 - 并发与线程:Redis 6+ 支持 I/O 线程;避免阻塞型命令;控制慢查询,监控
slowlog
。 - 架构层面:读写分离、主从复制、哨兵高可用、Cluster 分片水平扩展;客户端连接池与重试熔断。
- 系统调优:网络参数(
tcp_backlog
、somaxconn
)、net.core
、vm.overcommit_memory
等。
98.OSI 七层模型是什么?
展开 中等 VIP 网络
- 应用层(Application Layer)
- 作用:直接面向用户,提供网络服务的接口。
- 例子:HTTP、FTP、SMTP、DNS
- 数据单位:消息(Message)
- 表示层(Presentation Layer)
- 作用:处理数据的表示、编码、压缩、加密等,使不同系统能正确理解数据。
- 例子:JPEG、GIF、TLS/SSL、JSON 编码、Base64
- 数据单位:消息(Message)
- 会话层(Session Layer)
- 作用:建立、管理和终止通信会话;同步检查点。
- 例子:RPC、SQL 会话、NetBIOS
- 数据单位:消息(Message)
- 传输层(Transport Layer)
- 作用:端到端的可靠(TCP)或不可靠(UDP)数据传输;分段、重传、流量控制、拥塞控制。
- 例子:TCP、UDP
- 数据单位:段(Segment)
- 网络层(Network Layer)
- 作用:负责主机到主机的逻辑寻址与路径选择。
- 例子:IP(IPv4/IPv6)、ICMP、IGMP
- 数据单位:包(Packet)
- 数据链路层(Data Link Layer)
- 作用:在物理层上提供无差错的数据传输;进行帧定界、差错检测(CRC)。
- 例子:Ethernet、PPP、HDLC、MAC 地址
- 数据单位:帧(Frame)
- 物理层(Physical Layer)
- 作用:定义物理媒介、信号、电压、线缆、接口标准等。
- 例子:RJ45、光纤、IEEE 802.3、双绞线
- 数据单位:比特(Bit)
99.说说 AQS 吧?
展开 中等 Java并发 Java
AbstractQueuedSynchronizer
:基于状态值state
与 CLH 同步队列,提供独占/共享两种获取/释放语义。- 关键方法:
tryAcquire/tryRelease
(独占),tryAcquireShared/tryReleaseShared
(共享),配合 CAS +LockSupport.park/unpark
挂起/唤醒线程。 - 公平/非公平策略:决定获取顺序是否严格 FIFO。
- 基于 AQS 的组件:
ReentrantLock
、Semaphore
、CountDownLatch
、ReentrantReadWriteLock
、FutureTask
等。
100.Cookie、Session、Token 之间有什么区别?
展开 中等 VIP 网络
- 存储位置:
- Cookie:浏览器本地(可
HttpOnly
、Secure
、SameSite
)。 - Session:服务端存储会话状态(客户端通常持有
SessionID
Cookie)。 - Token:一般存于客户端(Header/Storage/Cookie),服务端可无状态校验(如 JWT)。
- Cookie:浏览器本地(可
- 状态性与伸缩:
- Session 需要共享或粘性会话;Token 天生适合无状态与横向扩展。
- 安全性:
- 防 CSRF:优先使用
SameSite
或在 Header 传递 Token;结合 CSRF Token 双重校验。 - 防 XSS:Cookie 加
HttpOnly
;Token 泄露需及时失效(黑名单/短有效期/刷新机制)。
- 防 CSRF:优先使用
- 适用场景:
- 传统站点:Session + Cookie 简单直接。
- 多端与微服务:JWT/OAuth2 的 Token 更合适(便于网关与下游服务校验)。