微服务相关?
1. 什么是微服务?它的核心思想是什么?
-
微服务:一种架构风格,将一个庞大的单体应用,拆分成多个小型、独立、可部署的服务,每个服务聚焦一个具体的业务模块,服务之间通过轻量级通信协议(如 HTTP、RPC)交互,独立开发、独立部署、独立维护。
-
核心思想:拆分巨石应用,实现高内聚、低耦合 ------ 每个服务只负责自己的业务,不依赖其他服务的内部实现,降低开发、测试、部署的复杂度,提升系统的可扩展性和容错性
2. 微服务和单体架构的区别是什么?
- 单体架构:所有业务模块(如用户、订单、商品)都打包在一个应用中,部署在一个服务器上,开发简单、部署方便,但耦合度高、扩展性差,一个模块故障可能导致整个应用崩溃。
- 微服务架构:拆分多个独立服务,每个服务对应一个业务模块,单独部署、单独扩容,耦合度低、扩展性强,一个服务故障不影响其他服务,但开发、部署、运维复杂度提升。
- 核心区别:是否拆分服务、耦合度高低、扩展性强弱、运维复杂度。
| 对比维度 | 单体架构 | 微服务架构 |
|---|---|---|
| 架构特点 | 所有模块集中在一个应用内 | 按业务拆分为多个独立服务 |
| 耦合度 | 高耦合,模块间依赖强 | 低耦合,服务间通过轻量协议通信 |
| 扩展性 | 整体扩容,难以针对模块单独扩展 | 可针对单个服务独立扩容 |
| 部署方式 | 单次部署整个应用 | 各服务可独立部署、上线 |
| 故障影响 | 一个模块故障可能导致整体宕机 | 单个服务故障不影响其他服务 |
| 开发运维 | 开发简单,运维成本低 | 开发、部署、运维复杂度高,需配套工具 |
微服务更适合业务快速迭代、高并发的场景,单体更适合业务简单、快速上线的小型项目
3.微服务的优势和缺点是什么?
优势:
- 低耦合:服务独立,互不依赖内部实现,修改一个服务不影响其他服务。
- 高扩展性:单个服务可根据业务需求独立扩容(如订单服务高峰期单独加机器)。
- 容错性强:一个服务故障,不会导致整个系统崩溃,可通过降级、熔断避免连锁反应。
- 开发高效:多团队并行开发,每个团队负责一个服务,提升开发效率。
缺点:
- 运维复杂:多个服务独立部署、监控、维护,需要额外的运维工具和成本。
- 分布式问题:服务之间通信存在网络延迟、超时、分布式事务等问题。
- 调试困难:跨服务问题排查复杂,需要追踪整个调用链路。
4.微服务核心组件?
微服务架构中,常用的核心组件有哪些?各自作用是什么?
服务注册与发现:
核心组件:Eureka、Consul、Nacos(常用 Nacos,易部署、功能全)。
作用:服务启动时,自动注册到注册中心,其他服务通过注册中心获取服务地址,无需手动配置服务地址,解决 "服务地址动态变化" 问题。
| 组件类别 | 常用组件 | 核心作用 |
|---|---|---|
| 服务注册与发现 | Eureka、Consul、ZooKeeper、Nacos | 管理所有服务的实例地址,实现服务自动注册、健康检查和动态发现,解决服务地址变化问题 |
| 配置中心 | Spring Cloud Config、Nacos | 集中管理所有服务的配置,支持配置动态刷新,避免配置文件分散和修改重启服务的问题 |
| 服务网关 | Zuul、Spring Cloud Gateway | 作为系统统一入口,实现路由转发、权限校验、限流、日志统计,隔离内外网,简化客户端调用 |
| 服务调用 / 通信 | RestTemplate、OpenFeign、Dubbo、 | 实现服务之间的远程调用,支持负载均衡,简化跨服务接口调用代码编写 |
| 熔断与限流 | Hystrix、Sentinel | 防止服务雪崩,当某个服务故障时快速熔断降级,保护上游服务,提升系统容错能力 |
| 链路追踪 | Zipkin、pinpoint | 追踪跨服务调用的完整链路,记录请求耗时、调用关系,快速定位分布式系统中的性能瓶颈和故障 |
API 网关:
- 核心组件:Gateway、Zuul(常用 Gateway,基于 Spring Boot,性能好)。
- 作用:统一入口,所有客户端请求都经过网关,实现路由转发、权限校验、限流、熔断、日志监控等功能,简化客户端调用。
服务通信:
- 核心方式:HTTP 协议(RESTful API,简单易懂,跨语言)、RPC 协议(Dubbo、Feign+Spring Cloud LoadBalancer,Feign 基于 HTTP,Dubbo 基于 RPC,性能高,适合 Java 内部服务通信)。
- 作用:实现不同微服务之间的数据交互和调用。
配置中心:
- Nacos、Nacos,兼顾注册中心和配置中心。
- 作用:集中管理所有微服务的配置(如数据库地址、端口、参数),支持配置动态刷新,无需重启服务即可更新配置。
什么是服务注册与发现?Nacos 的核心作用是什么?
- 服务注册:微服务启动时,将自己的服务名称、IP 地址、端口等信息,注册到注册中心(如 Nacos),注册中心维护一个服务列表。
- 服务发现:当服务 A 需要调用服务 B 时,先从注册中心查询服务 B 的地址,再通过该地址调用服务 B,无需手动配置服务 B 的地址。
- Nacos 核心作用:① 服务注册与发现(核心);② 配置中心(集中管理配置,动态刷新);③ 服务健康监测(检测服务是否可用,剔除故障服务)。
API 网关的核心功能有哪些?
- 路由转发 :将客户端请求,根据请求路径转发到对应的微服务(如
/api/user转发到用户服务,/api/order转发到订单服务)。 - 权限校验:统一拦截所有请求,校验用户 Token、权限,拒绝非法请求,无需每个服务单独实现权限校验。
- 限流熔断:限制客户端请求频率,避免服务被高并发压垮;当目标服务故障时,熔断调用,返回默认数据,避免连锁故障。
- 日志监控:记录所有请求的详细信息(请求路径、响应时间、状态码),便于问题排查和性能分析
微服务服务治理?
什么是服务熔断、服务降级?两者的区别是什么?
-
服务熔断:当一个服务出现频繁故障、超时,为了避免影响其他服务,暂时 "断开" 对该服务的调用,返回默认数据或提示,等待服务恢复后再重新调用(类似电路熔断,防止故障扩散)。
- 核心目的:防止故障连锁反应,保护整个微服务系统。
-
服务降级:当系统处于高并发、资源紧张时,暂时关闭一些非核心服务的功能(如积分兑换、历史订单查询),优先保证核心服务(如支付、下单)正常运行,降低系统负载。
- 核心目的:保证核心服务可用,牺牲非核心功能。
-
区别:熔断是 "被动触发"(服务故障导致),降级是 "主动触发"(高并发、资源紧张导致)。
什么是负载均衡?微服务中如何实现负载均衡?
-
负载均衡:当一个服务部署多个实例(如订单服务部署 3 台机器),客户端请求会均匀分配到各个实例,避免单个实例压力过大,提升服务的可用性和并发能力。
-
微服务中实现方式(常用 2 种):
- 客户端负载均衡:Feign+Spring Cloud LoadBalancer(Spring Cloud 官方推荐,替代 Ribbon),客户端调用服务时,从注册中心获取服务实例列表,自己决定调用哪个实例(如轮询、随机)。
- 服务端负载均衡:API 网关(如 Spring Cloud Gateway),所有请求经过网关,由网关将请求分配到不同的服务实例。
微服务中的分布式事务问题是什么?如何解决?
-
分布式事务问题:多个微服务之间的事务操作,无法保证原子性(如用户下单:订单服务创建订单,库存服务扣减库存,支付服务扣减余额,若其中一个服务失败,其他服务已执行的操作无法回滚,导致数据不一致)。
-
解决方法:
- 最终一致性方案(主流):基于消息队列(如 RabbitMQ、RocketMQ)实现,采用 "本地事务 + 消息通知" 的方式,确保所有服务最终执行成功(如订单创建成功后,发送消息通知库存和支付服务,失败则重试)。
- 简化方案:使用 Seata 框架(阿里开源),封装了分布式事务的实现,只需简单配置,即可实现分布式事务的原子性
什么是服务链路追踪?常用组件是什么?
-
服务链路追踪:当一个客户端请求经过多个微服务(如请求下单→订单服务→库存服务→支付服务),追踪整个请求的调用链路,记录每个服务的调用时间、状态,便于排查跨服务的问题(如请求超时,定位是哪个服务耗时过长)。
-
常用组件:Sleuth+Zipkin(Spring Cloud 常用),Sleuth 负责生成追踪 ID,标记调用链路;Zipkin 负责收集追踪数据,可视化展示调用链路。
5.微服务高频框架(Spring Cloud/Spring Boot)
Spring Boot 和 Spring Cloud 的关系是什么?
-
Spring Boot:一款快速开发框架,简化 Spring 应用的配置和部署("约定大于配置"),可快速创建独立的 Spring 应用,是微服务开发的基础(每个微服务都是一个 Spring Boot 应用)。
-
Spring Cloud:基于 Spring Boot,提供了微服务架构所需的核心组件(服务注册发现、网关、Spring Cloud LoadBalancer 负载均衡等),是一套微服务解决方案,用于整合多个 Spring Boot 应用,构建完整的微服务系统。
-
关系:Spring Boot 是基础,Spring Cloud 是在 Spring Boot 之上的微服务整合框架,没有 Spring Boot 就没有 Spring Cloud,Spring Cloud 依赖 Spring Boot 实现微服务的开发和部署
- Spring Boot:是一个快速开发单体应用的脚手架,通过自动配置简化了 Spring 应用的搭建和开发,让你可以快速创建独立运行的 Java 应用。
- Spring Cloud:是一套基于 Spring Boot 实现的微服务治理框架,提供了服务注册与发现、配置中心、网关、负载均衡、熔断限流等微服务所需的组件和解决方案。
- 两者关系 :Spring Cloud 依赖 Spring Boot,它的所有组件都是基于 Spring Boot 开发的;Spring Boot 是 Spring Cloud 的基础,Spring Cloud 是 Spring Boot 在微服务场景下的扩展。简单来说,Spring Boot 用于开发单个微服务,Spring Cloud 用于管理多个微服务。
Spring Cloud 常用组件有哪些?
- 服务注册与发现:Nacos/Eureka(Nacos 更常用,兼容 Eureka,支持配置中心)。
- API 网关:Spring Cloud Gateway(替代 Zuul,性能更好,基于 Netty)。
- 服务通信:Feign(基于 HTTP,声明式调用,简化服务调用)、Dubbo(基于 RPC,性能高)。
- 负载均衡:Spring Cloud LoadBalancer(Spring Cloud 官方推荐,替代 Ribbon,配合 Feign 使用,实现客户端负载均衡)。
- 熔断降级:Sentinel(阿里开源,替代 Hystrix,实现熔断、降级、限流)。
- 配置中心:Nacos(Nacos 兼顾注册中心,部署简单)。
Feign 和 Dubbo 的区别是什么?
- 通信协议:Feign 基于 HTTP 协议(RESTful API),跨语言、简单易懂;Dubbo 基于 RPC 协议,性能更高,仅适用于 Java 语言。
- 开发复杂度:Feign 是声明式调用,只需编写接口和注解,开发简单;Dubbo 需要配置服务接口、注册中心,开发略复杂。
- 适用场景:Feign 适合跨语言、服务之间通信频率不高的场景;Dubbo 适合 Java 内部服务、高并发、高性能要求的场景。
| 对比维度 | Feign | Dubbo |
|---|---|---|
| 通信协议 | HTTP(RESTful) | RPC |
| 跨语言支持 | 支持 | 仅 Java |
| 性能 | 一般 | 高 |
| 开发方式 | 声明式接口 + 注解,简单 | 需配置服务接口、注册中心,略复杂 |
| 适用场景 | 跨语言调用、通信频率不高 | Java 内部服务、高并发高性能场景 |
微服务拆分的原则是什么?
- 单一职责原则:每个服务只负责一个核心业务模块(如用户服务只处理用户相关操作,订单服务只处理订单相关操作)。
- 高内聚、低耦合:服务内部功能紧密相关,服务之间尽量减少依赖,仅通过接口交互。
- 避免过度拆分:拆分过细会导致服务数量过多,运维、通信成本增加(如不要把 "用户查询" 和 "用户修改" 拆分成两个服务)。
- 按业务域拆分:以业务领域为边界(DDD 领域驱动设计思想),比如电商系统可拆分为用户域、订单域、支付域、商品域等,每个服务对应一个独立的业务域。
- 避免过度拆分:拆分粒度要适中,过细的拆分会导致服务间调用链路过长、运维复杂度陡增,反而得不偿失。
- 数据独立自治:每个微服务应拥有自己独立的数据库,避免跨服务直接访问数据库,保证数据的内聚性和一致性。
- 面向接口而非实现:服务间仅通过公开接口交互,隐藏内部实现细节,避免服务间的强依赖。
微服务中,服务之间为什么不能直接调用,要通过 API 网关?
- 统一入口:客户端无需记住多个服务的地址,只需调用网关地址,由网关转发,简化调用。
- 统一管控:所有请求都经过网关,可统一实现权限校验、限流、熔断、日志监控,无需每个服务单独实现,降低开发成本。
- 隐藏服务细节:客户端无需知道服务的部署地址、实例数量,服务升级、扩容时,客户端无需修改任何配置。
Nacos 和 Eureka 的区别是什么?
- 功能:Nacos 支持服务注册发现、配置中心、服务健康监测,功能更全面;Eureka 仅支持服务注册发现。
- 可用性:Nacos 支持 AP/CP 模式切换(默认 AP,保证可用性);Eureka 仅支持 AP 模式,注重可用性。
- 部署:Nacos 部署简单,支持单机、集群模式;Eureka 需要单独部署,集群配置较复杂。
- 现状:Eureka 已停止更新,Nacos 是 Spring Cloud Alibaba 推荐组件,目前更常用。
| 对比维度 | Nacos | Eureka |
|---|---|---|
| 核心功能 | 服务注册发现 + 配置中心 + 健康监测 | 仅服务注册发现 |
| 一致性模式 | 支持 AP/CP 切换 | 仅支持 AP |
| 部署复杂度 | 简单,支持一键启动 | 较复杂,需手动配置集群 |
| 社区活跃度 | 活跃,持续更新维护 | 已停止更新 |
| Spring Cloud 支持 | Spring Cloud Alibaba 推荐组件 | Spring Cloud 早期默认组件 |
微服务中的 "雪崩效应" 是什么?如何避免?
雪崩效应:一个核心服务故障,导致依赖该服务的其他服务也相继故障,最终导致整个微服务系统崩溃(如支付服务故障,导致订单、库存、用户服务都无法正常运行)。
避免方法
- 熔断:给服务添加熔断机制(如 Sentinel),故障时断开调用,避免故障扩散。
- 降级:高并发时,降级非核心服务,保证核心服务可用。
- 限流:通过 API 网关或 Sentinel 限制客户端请求频率,避免服务被高并发压垮。
- 服务集群:核心服务部署多个实例,配合 Spring Cloud LoadBalancer 实现负载均衡,避免单点故障。
- 服务熔断:当依赖的服务故障率达到阈值时,快速熔断,直接返回降级结果,避免持续等待造成线程资源耗尽。
- 服务降级:高并发或故障时,暂时关闭非核心功能,优先保证核心服务可用。
- 限流控制:限制请求频率,防止超出系统承载能力,避免服务被压垮。
- 超时与重试机制:设置合理的调用超时时间,避免线程被长时间占用;同时限制重试次数,防止请求风暴。
- 异步调用:使用消息队列进行解耦,避免同步调用链路过长导致的级联故障。
- 资源隔离:不同服务的调用使用独立的线程池,避免单个服务故障拖垮整个应用。
Java 锁
解决多线程并发安全问题的核心机制,用来控制多个线程同时访问共享资源时的冲突,避免数据错乱、线程安全问题。
作用:修饰方法、代码块,保证同一时间只有一个线程执行。
Java 分布式锁
作用:修饰方法、代码块,保证同一时间只有一个线程执行。
分布式锁是解决分布式系统中多服务 / 多进程并发安全 的核心方案,解决:同一个服务部署在多台机器上,同时操作共享资源(如扣库存、生成订单)导致的数据错乱问题。
和本地锁(synchronized/Lock)最大区别:
- 本地锁:只锁当前 JVM 进程,分布式环境无效
- 分布式锁:锁整个集群 / 全局 ,跨机器、跨服务生效
- 为什么本地锁(synchronized/ReentrantLock)不能用于分布式场景?
答案 :本地锁作用域仅限单个 JVM 进程,分布式系统中服务部署在多台机器 / 多个进程,不同进程的锁相互隔离,无法实现全局互斥,因此失效。
- 分布式锁需要满足哪些核心特性?
- 互斥性:同一时刻仅一个客户端持有锁;
- 防死锁:锁必须设置过期时间,客户端宕机也能自动释放;
- 安全性:只能释放自己持有的锁,防止误删;
- 高可用:加锁、解锁操作不能因中间件故障大面积失效;
- 高性能:加解锁开销小,支撑高并发;
- 可选:支持可重入、阻塞等待、锁续期。
3. 主流分布式锁实现方案有哪些?各自优缺点?
答案:
- Redis 分布式锁
- 优点:性能高、实现简单、社区成熟,适配绝大多数业务;
- 缺点:主从异步复制可能短暂丢锁,强一致性弱于 Zookeeper。
- Zookeeper 分布式锁
- 优点:基于临时节点 + 监听机制,强一致性、天然防死锁、无需手动续期;
- 缺点:性能偏低,集群部署重,高并发场景不占优。
- MySQL 分布式锁
- 优点:零额外组件,实现最简单;
- 缺点:性能差、数据库压力大、容易出现死锁,仅适用于低并发简单场景。
4.手写 Redis 分布式锁,为什么要用 SET key value NX PX 一条命令?分开写行不行?
答案 : 不行。set+expire 是两条命令,不具备原子性。 如果执行完set后服务宕机,锁永远无法过期 ,引发死锁。 SET NX PX 是 Redis 原子命令,加锁 + 设置过期一步完成,彻底规避该问题。
NX:key 不存在才设置,保证互斥;PX:毫秒级过期,防死锁。
性能指标
一、系统性能核心指标
- QPS(Queries Per Second)每秒查询率
定义:每秒服务器可响应的查询请求数,偏向读请求。
适用:列表查询、页面访问、纯查询接口。
特点:单次逻辑简单、耗时短。
- TPS(Transactions Per Second)每秒事务数
定义:每秒完成的完整业务事务数,偏向写请求。
事务:一次完整业务流程,包含多次读写、接口调用、数据变更。
适用:下单、支付、数据提交、增删改业务。
特点:链路长、资源消耗大,同场景下 TPS 通常低于 QPS。
- 吞吐量
广义概念,分两类:
请求吞吐量:统计每秒总请求数,和 QPS 口径接近;
数据吞吐量:单位时间传输数据大小 (KB/s、MB/s),多用于文件、流媒体。
配套监控指标
响应时间 RT、并发数、错误率、CPU / 内存 / 磁盘 IO / 网络 IO、数据库连接数。
吞吐量 = 单位时间里,系统成功处理 / 通过的数据、任务、产品总量 简单一句话:单位时间能干完多少活。
1. 网络 / 通信(最常用)
指每秒成功传输的数据量,单位:bps、MB/s、Gbps
例:宽带吞吐量 100MB/s = 每秒实际传 100 兆数据(≠带宽标称,受损耗影响)
2. 计算机 / 服务器 / 接口
单位时间处理请求数,常用:TPS(每秒事务数)、QPS(每秒查询数)
例:接口吞吐量 5000QPS = 接口一秒能正常扛 5000 次访问
3. 生产 / 物流 / 工厂
单位时间产出成品 / 转运货物数量
例:流水线日吞吐量 5000 件 = 一天生产 5000 个产品
4. 硬盘 / 存储
每秒读写数据大小,MB/s
固态硬盘吞吐量 5000MB/s:一秒读写 5000 兆数据
缓存三大问题
缓存穿透、缓存击穿、缓存雪崩
整体场景:请求优先查询缓存,缓存无数据再查询数据库。
(一)缓存穿透
定义:查询缓存、数据库都不存在的数据,无效请求全部直达数据库。
成因:恶意请求非法参数、业务查询无结果数据。
解决方案:
空值缓存:查询为空时,缓存空数据,设置较短过期时间;
参数校验:网关 / 接口拦截非法 ID、格式异常请求;
布隆过滤器:前置过滤非法 Key,海量数据场景优选;
限流:对异常 IP、高频无效请求做流量限制。
(二)缓存击穿
定义:单个热点 Key过期,瞬时大量并发请求直接打到数据库。
成因:秒杀、爆款、首页等高访问热点数据统一过期。
解决方案:
分布式互斥锁:仅一个请求查询数据库并更新缓存,其余请求等待;
热点 Key 永不过期:代码异步定时刷新数据,规避过期问题;
逻辑过期:存储逻辑过期时间,过期后异步更新缓存,旧数据正常返回。
(三)缓存雪崩
定义:大批量缓存 Key 同时失效 或 Redis 整体宕机,海量请求压垮数据库。
成因:Key 过期时间集中、Redis 集群故障、服务资源耗尽。
解决方案:
过期时间加随机值,打散批量失效;
Redis 做集群 + 哨兵,实现高可用,避免单点故障;
多级缓存:本地缓存 + 分布式缓存,层层兜底;
限流、熔断、降级:缓存异常时拒绝流量,返回兜底数据;
线程池隔离:隔离缓存、数据库请求,防止故障扩散。
快速区分口诀
穿透:查不存在的数据 → 无效请求打 DB
击穿:单个热点 Key 失效 → 高并发单点打 DB
雪崩:批量 Key 失效 / 缓存宕机 → 全量流量打 DB
谈谈你对线程的理解?
线程是 CPU 调度和执行的最小单位,一个进程里可以有多个线程,它们共享进程内存,同时执行不同任务,从而提高程序效率和响应速度。
- 进程:一个正在运行的程序(比如一个 Java 应用)
- 线程:程序里真正干活的 "执行小路"
进程 vs 线程
- 进程有独立内存空间,相互隔离;
- 线程共享进程内存(堆、方法区),只私有栈、程序计数器;
- 进程切换开销大,线程切换开销小;
- 多进程是 "多个程序同时跑",多线程是 "一个程序里多段代码同时跑"。
线程的生命周期
- 新建(New):new Thread (),还没 start
- 就绪(Runnable):调用 start (),等待 CPU 时间片
- 运行(Running):获得 CPU,执行 run ()
- 阻塞(Blocked):等待锁、IO、sleep 等
- 等待(Waiting):wait()、join()、LockSupport.park()
- 终止(Terminated):run 执行完毕或异常退出
线程从新建,start()后进入就绪,抢到 CPU 时间片进入运行;遇到同步锁、sleep/wait/park等操作进入阻塞 / 等待;任务执行完毕或异常退出后线程终止。
Java 创建线程的 4 种方式详解
继承Thread类
实现Runnable接口(无返回值)
实现Callable接口(有返回值、可抛出受检异常)
使用线程池(ThreadPoolExecutor/Executors)✅企业主流
start()和run()区别:start()真正创建操作系统线程、进入就绪;直接调用run()只是普通方法调用,不会新开线程。- 线程池是项目开发首选,前三种多用于临时简单测试场景。
线程常用方法
start():启动线程,进入就绪态run():承载业务逻辑sleep(long):线程休眠,不释放锁wait():进入等待,释放锁 ,依靠notify/notifyAll唤醒notify()/notifyAll():唤醒处于等待的线程join():线程插队执行,等待目标线程执行完毕后当前线程再继续yield():让出 CPU 使用权,线程退回就绪态
多线程带来的问题与解决
多线程核心问题:原子性、可见性、有序性缺失 → 引发线程不安全、数据错乱
解决方案:
synchronized:内置同步锁,可保证原子性、可见性、有序性Lock:以ReentrantLock为代表,锁操作更灵活volatile:保证可见性、禁止指令重排,无法保证原子性ThreadLocal:变量线程私有化,规避多线程共享竞争- 线程安全集合:
ConcurrentHashMap、CopyOnWriteArrayList等
一、手动创建线程弊端
- 频繁创建销毁开销大
- 线程不可控,容易 OOM
二、线程池优势
- 复用线程
- 控制并发数量
- 统一管理、配置拒绝策略
三、线程池核心参数
核心线程数、最大线程数、阻塞队列、空闲存活时间、线程工厂、拒绝策略
总结:
- 线程是 CPU 调度最小单位,共享进程内存,提升并发效率。
- 线程生命周期:新建→就绪→运行→阻塞 / 等待→终止。
- 创建线程 4 种方式:继承 Thread、实现 Runnable、实现 Callable、使用线程池。
- 开启线程必须调用
start(),直接调用run()只是普通方法执行。 - 多线程安全根源:原子性、可见性、有序性问题。
- 解决方案:synchronized、Lock、volatile、ThreadLocal、线程安全集合。
- 项目开发优先使用线程池,规避频繁创建线程带来的性能隐患。
Spring
Spring 两大核心:IOC(控制反转)、AOP(面向切面编程)
Bean 生命周期: 实例化→填充属性→初始化(afterPropertiesSet、init-method)→使用→销毁 (destroy)
IOC概念:对象创建、依赖交给 Spring 容器管理,不再 new,反转控制权。
AOP概念:横向抽取公共代码(日志、事务、权限),不改动原业务代码增强功能。
五大通知:前置、后置、异常、最终、环绕通知。
动态代理:
- 目标类实现接口:JDK 动态代理
- 没实现接口:CGLIB 字节码代理
@Autowired 和 @Resource 区别
- @Autowired:Spring 注解,默认按类型注入,配合 @Qualifier 按名称
- @Resource:JSR 规范,默认先按名称、再按类型
@Transactional 失效场景
- 方法不是 public
@Transactional 基于 AOP 代理,非 public (private/protected) 不会生成代理,事务失效。
2.同类中内部调用
原因:this调用不走代理对象,不会切入事务。 解决:从容器拿本类代理对象再调用。
-
异常被 try-catch 吃掉,没有抛出异常
-
指定了异常,抛出类型不匹配
5.数据库引擎不支持事务
表引擎是 MyISAM(不支持事务),换成 InnoDB。
6.传播行为导致不开启事务
例如:@Transactional(propagation = Propagation.SUPPORTS) 外层无事务,当前方法以非事务运行,出错不回滚。
@Transactional 失效口诀(好记)
私类自捕引擎错,传播配置不生效
逐句释义:
- 私:方法非 public,无法 AOP 代理
- 类:同类 this 内部调用,没走代理
- 自捕:异常 try-catch 捕获,未向外抛出
- 引擎:数据表 MyISAM 引擎不支持事务
- 错:rollbackFor 指定异常和抛出异常不一致
- 传播:传播属性配置不当(SUPPORTS 等无事务运行)
开发规范一句话
统一注解:@Transactional(rollbackFor = Exception.class)
SpringMVC 执行流程
口诀:请映适控视
请求→映射→适配→控制器→视图解析
完整 5 步流程
- DispatcherServlet 接收请求(前端控制器,入口)
- 处理器映射器 HandlerMapping:根据 URL 找到对应 Controller
- 处理器适配器 HandlerAdapter:执行 Controller 方法
- Controller:执行业务,返回 ModelAndView
- 视图解析器 ViewResolver:解析视图地址,渲染页面返回浏览器
精简面试一句话
请求经过 DispatcherServlet,由映射找控制器、适配器执行,控制器返回数据视图,视图解析后响应页面。