招行Java 面试知识点梳理:Spring 事务、锁、线程池、集合

1. Spring 事务的实现方式

Spring 事务本质上是对底层数据库事务(JDBC / JPA / MyBatis 等)的统一抽象,常见有三种实现方式。

1.1 编程式事务

通过 TransactionTemplate 或 PlatformTransactionManager 手动控制事务边界,代码侵入性强,但控制粒度最细。

java 复制代码
@Service
public class OrderService {

    @Resource
    private TransactionTemplate transactionTemplate;

    public void createOrder(Order order) {
        transactionTemplate.execute(status -> {
            orderMapper.insert(order);
            stockMapper.decrease(order.getSkuId(), order.getCount());
            return null;
        });
    }
}

1.2 声明式事务(XML 配置)

早期 Spring 项目通过 <tx:advice> + AOP 切点配置事务,现在基本被注解方式取代,了解即可。

1.3 声明式事务(注解方式)

最常用的方式,在方法或类上标注 @Transactional,由 Spring AOP 生成代理对象织入事务逻辑。

java 复制代码
@Service
public class OrderService {

    @Transactional(rollbackFor = Exception.class)
    public void createOrder(Order order) {
        orderMapper.insert(order);
        stockMapper.decrease(order.getSkuId(), order.getCount());
    }
}

2. @Transactional 的实现原理

@Transactional 依赖 Spring AOP 的动态代理实现,核心流程如下:
#mermaid-svg-qasomn4x9Kb5ROT9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qasomn4x9Kb5ROT9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qasomn4x9Kb5ROT9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qasomn4x9Kb5ROT9 .error-icon{fill:#552222;}#mermaid-svg-qasomn4x9Kb5ROT9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qasomn4x9Kb5ROT9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qasomn4x9Kb5ROT9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qasomn4x9Kb5ROT9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qasomn4x9Kb5ROT9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qasomn4x9Kb5ROT9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qasomn4x9Kb5ROT9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qasomn4x9Kb5ROT9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qasomn4x9Kb5ROT9 .marker.cross{stroke:#333333;}#mermaid-svg-qasomn4x9Kb5ROT9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qasomn4x9Kb5ROT9 p{margin:0;}#mermaid-svg-qasomn4x9Kb5ROT9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qasomn4x9Kb5ROT9 .cluster-label text{fill:#333;}#mermaid-svg-qasomn4x9Kb5ROT9 .cluster-label span{color:#333;}#mermaid-svg-qasomn4x9Kb5ROT9 .cluster-label span p{background-color:transparent;}#mermaid-svg-qasomn4x9Kb5ROT9 .label text,#mermaid-svg-qasomn4x9Kb5ROT9 span{fill:#333;color:#333;}#mermaid-svg-qasomn4x9Kb5ROT9 .node rect,#mermaid-svg-qasomn4x9Kb5ROT9 .node circle,#mermaid-svg-qasomn4x9Kb5ROT9 .node ellipse,#mermaid-svg-qasomn4x9Kb5ROT9 .node polygon,#mermaid-svg-qasomn4x9Kb5ROT9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qasomn4x9Kb5ROT9 .rough-node .label text,#mermaid-svg-qasomn4x9Kb5ROT9 .node .label text,#mermaid-svg-qasomn4x9Kb5ROT9 .image-shape .label,#mermaid-svg-qasomn4x9Kb5ROT9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-qasomn4x9Kb5ROT9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qasomn4x9Kb5ROT9 .rough-node .label,#mermaid-svg-qasomn4x9Kb5ROT9 .node .label,#mermaid-svg-qasomn4x9Kb5ROT9 .image-shape .label,#mermaid-svg-qasomn4x9Kb5ROT9 .icon-shape .label{text-align:center;}#mermaid-svg-qasomn4x9Kb5ROT9 .node.clickable{cursor:pointer;}#mermaid-svg-qasomn4x9Kb5ROT9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qasomn4x9Kb5ROT9 .arrowheadPath{fill:#333333;}#mermaid-svg-qasomn4x9Kb5ROT9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qasomn4x9Kb5ROT9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qasomn4x9Kb5ROT9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qasomn4x9Kb5ROT9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qasomn4x9Kb5ROT9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qasomn4x9Kb5ROT9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qasomn4x9Kb5ROT9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qasomn4x9Kb5ROT9 .cluster text{fill:#333;}#mermaid-svg-qasomn4x9Kb5ROT9 .cluster span{color:#333;}#mermaid-svg-qasomn4x9Kb5ROT9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qasomn4x9Kb5ROT9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qasomn4x9Kb5ROT9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-qasomn4x9Kb5ROT9 .icon-shape,#mermaid-svg-qasomn4x9Kb5ROT9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qasomn4x9Kb5ROT9 .icon-shape p,#mermaid-svg-qasomn4x9Kb5ROT9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qasomn4x9Kb5ROT9 .icon-shape .label rect,#mermaid-svg-qasomn4x9Kb5ROT9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qasomn4x9Kb5ROT9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qasomn4x9Kb5ROT9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qasomn4x9Kb5ROT9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
否
调用 orderService.createOrder()
是否被代理
直接执行,无事务
TransactionInterceptor.invoke()
TransactionManager 获取/创建事务
执行业务方法
是否抛出异常
按 rollbackFor 规则回滚
提交事务

关键点:

  • 代理方式 :目标类实现了接口时默认用 JDK 动态代理,否则用 CGLIB 生成子类代理(Spring Boot 2.x 起默认 proxyTargetClass=true,统一走 CGLIB)。
  • 拦截器 :TransactionInterceptor 实现 MethodInterceptor,在方法调用前后完成事务的开启、提交、回滚。
  • 事务信息解析 :TransactionAttributeSource 解析方法或类上的 @Transactional 属性(传播行为、隔离级别、超时、只读、回滚规则)。
  • 事务管理器 :PlatformTransactionManager 具体实现(如 DataSourceTransactionManager)负责与数据库交互,本质是设置 autoCommit=false 并执行 commit / rollback。

3. 事务常见失效场景

失效场景 原因 解决方式
同类内部方法调用 未经过代理对象,AOP 不生效 注入自身代理、拆到另一个 Bean、或用 AopContext.currentProxy()
方法非 public Spring 事务默认只拦截 public 方法 改为 public,或使用 AspectJ 模式
异常被 catch 吞掉 拦截器感知不到异常 捕获后重新抛出,或手动 setRollbackOnly()
抛出受检异常 默认只对 RuntimeException 和 Error 回滚 指定 rollbackFor = Exception.class
类未被 Spring 管理 不是 Bean,无法生成代理 加上 @Service / @Component
数据库引擎不支持事务 如 MyISAM 使用 InnoDB
传播行为配置不当 如 NOT_SUPPORTED、NEVER 按业务选择 REQUIRED / REQUIRES_NEW
多线程中执行 事务绑定在 ThreadLocal 上,子线程不共享 子线程内单独开启事务

4. 事务的四大特性(ACID)

  • 原子性(Atomicity):事务内的操作要么全部成功,要么全部失败回滚,由 undo log 保证。
  • 一致性(Consistency):事务执行前后数据满足业务约束,是 ACID 的最终目标,由其他三个特性共同保障。
  • 隔离性(Isolation):并发事务之间互不干扰,由锁和 MVCC 实现,对应四种隔离级别:读未提交、读已提交、可重复读、串行化。
  • 持久性(Durability):事务提交后数据永久保存,由 redo log 保证。

4.1 四种隔离级别对应的并发问题

隔离级别越高,并发一致性越强,但并发性能越低。四种级别分别对应不同的并发异常:

隔离级别 脏读 不可重复读 幻读 说明
读未提交(Read Uncommitted) 可能 可能 可能 能读到其他事务未提交的数据,几乎不用
读已提交(Read Committed) 不会 可能 可能 只能读到已提交数据,Oracle、PostgreSQL 默认
可重复读(Repeatable Read) 不会 不会 InnoDB 下基本不会 同一事务内多次读取结果一致,MySQL 默认
串行化(Serializable) 不会 不会 不会 事务串行执行,性能最差
  • 脏读:读到另一个事务尚未提交、之后可能回滚的数据。
  • 不可重复读:同一事务内两次读取同一行,结果不同(其他事务提交了 UPDATE)。
  • 幻读:同一事务内两次按相同条件查询,第二次多出(或少了)行(其他事务提交了 INSERT / DELETE)。

4.2 各隔离级别的实现方式

  • 读未提交:不加任何读锁,直接读最新数据,因此可能读到未提交的脏数据。
  • 读已提交:读操作使用 MVCC 快照,每次查询都生成新的 ReadView,所以能看到其他事务已提交的最新修改。
  • 可重复读:事务开始时生成一次 ReadView 并复用,保证多次读取结果一致;InnoDB 通过 Next-Key Lock(记录锁 + 间隙锁)在锁定读时避免幻读。
  • 串行化:读操作加共享锁、写操作加排他锁,事务之间完全串行,冲突时后到的事务等待。

4.3 如何避免并发问题

  • 按业务选择合适级别:大多数业务用「读已提交」或「可重复读」即可,不要盲目追求串行化。
  • 显式指定隔离级别 :@Transactional(isolation = Isolation.REPEATABLE_READ),避免依赖数据库默认值带来的环境差异。
  • 关键读使用锁定读 :需要强一致时用 SELECT ... FOR UPDATE 或 LOCK IN SHARE MODE,把读纳入锁保护。
  • 用乐观锁兜底:对库存、余额等热点数据加版本号,提交时校验,避免长事务持锁。
  • 缩短事务范围:事务内不做远程调用、不等待用户输入,减少锁持有时间。
  • 避免大范围更新:批量更新拆小批次,减少间隙锁与死锁概率。
  • 统一加锁顺序:多表或多行更新时按固定顺序加锁,降低死锁风险。

5. 乐观锁与悲观锁

5.1 悲观锁

假设并发冲突一定会发生,操作前先加锁。数据库层面常用 SELECT ... FOR UPDATE。

sql 复制代码
SELECT * FROM product WHERE id = 1 FOR UPDATE;
UPDATE product SET stock = stock - 1 WHERE id = 1;

适合写多读少、冲突频繁的场景,但会阻塞其他事务,注意死锁风险。

5.2 乐观锁

假设冲突较少,提交时校验版本号或时间戳,失败则重试。

sql 复制代码
UPDATE product SET stock = stock - 1, version = version + 1
WHERE id = 1 AND version = #{version};

影响行数为 0 说明版本已被修改,需要重试或提示用户。适合读多写少的场景,无锁等待,但冲突高时重试成本上升。

6. 线程池与参数配置

6.1 核心参数

java 复制代码
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    4,                                  // corePoolSize 核心线程数
    8,                                  // maximumPoolSize 最大线程数
    60L, TimeUnit.SECONDS,              // keepAliveTime 空闲线程存活时间
    new ArrayBlockingQueue<>(200),      // workQueue 任务队列
    new ThreadFactory() {               // threadFactory 线程工厂
        private final AtomicInteger idx = new AtomicInteger(1);
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "biz-pool-" + idx.getAndIncrement());
        }
    },
    new ThreadPoolExecutor.CallerRunsPolicy() // handler 拒绝策略
);

6.2 参数设置思路

  • corePoolSize :CPU 密集型建议 CPU 核数 + 1;IO 密集型建议 CPU 核数 × (1 + 平均等待时间 / 平均计算时间)。
  • workQueue :有界队列防止内存溢出,ArrayBlockingQueue 或 LinkedBlockingQueue 指定容量。
  • 拒绝策略 :AbortPolicy 抛异常、CallerRunsPolicy 由调用线程执行、DiscardPolicy 静默丢弃、DiscardOldestPolicy 丢弃最老任务。核心业务建议自定义策略做降级或告警。
  • 线程工厂:自定义线程名,便于排查问题。
  • 监控 :通过 getActiveCount()、getQueue().size() 等指标接入监控,避免队列堆积。

7. Redis 数据结构与 Java 对应关系

Redis 结构 典型用途 Java 对应
String 缓存对象、计数器、分布式锁 String、序列化后的对象
Hash 存储对象字段 Map<String, String>
List 消息队列、时间线 LinkedList、Deque
Set 去重、共同好友 Set、HashSet
ZSet 排行榜、延时队列 TreeSet + 分值,或 Map<value, score>
Bitmap 签到、活跃统计 BitSet
HyperLogLog 基数统计(UV) 无直接对应,用 long 接收估算值
GEO 附近的人 自定义坐标对象

8. ArrayList 与 LinkedList 的区别及应用场景

对比项 ArrayList LinkedList
底层结构 动态数组 双向链表
随机访问 O(1) O(n)
头部插入/删除 O(n) O(1)
尾部插入 均摊 O(1) O(1)
内存占用 连续内存,有扩容冗余 每个节点额外存前后指针
扩容 默认 1.5 倍扩容并复制 无需扩容

应用场景:

  • ArrayList:查询多、尾部追加多的场景,如列表展示、批量数据装载,是绝大多数场景的默认选择。
  • LinkedList:频繁在头部或中间插入删除的场景,或需要当作队列/双端队列使用(Deque 接口)。

实际开发中,即使需要队列语义,也常优先使用 ArrayDeque,因为它的内存布局更紧凑、性能更好。

相关推荐
YDS8294 小时前
Small Spring IOC篇:作用域和FactoryBean
java·后端·spring
Shaoshing4 小时前
LeetCode100 42.接雨水
java·算法·leetcode
枫子有风4 小时前
卡码网盘项目深挖
java·redis·jwt·es·pgvector
郭小李4 小时前
springmvc 拦截器
java·开发语言·前端
大风起兮云飞扬丶4 小时前
Java——日期类
java·开发语言
Wang's Blog5 小时前
Java 项目实战:交友项目 - 联系人管理需求分析
java·开发语言·交友
aiautotools5 小时前
Claude Code MCP 怎么配:命令、scope 与避坑
面试
lazy H5 小时前
【计算机网络】第 13 篇:NAT 和端口映射是什么?为什么本地服务外网访问不了?
java·网络·网络协议·学习·计算机网络·408
学到老活到老5 小时前
第 22 集:Filter、Interceptor、AOP 都能拦截,为什么不能互换?
java·后端·spring