适用场景:后端开发面试、高并发系统设计

核心逻辑:基础语法 -> 线程安全  ->  资源复用(线程池) ->  分布式架构


1. 基础篇:生命周期与创建

1.1 创建线程

  • 继承 Thread:❌ 不推荐。Java 是单继承的,继承后无法扩展其他类。

  • 实现 Runnable 接口:✅ 推荐。实现解耦(任务与执行分离),方便后续配合线程池使用。

1.2 关键状态流转(高频考点)

  • RUNNABLE:包含 Ready (就绪) 和 Running (运行中)。

  • WAITING / TIMED_WAITING

    • 定义:线程主动让出 CPU,进入等待队列(不消耗 CPU)。

    • 触发wait(), join(), LockSupport.park()

    • 注意:这不是忙等(Busy-Wait),忙等通常发生在 CAS 自旋中。

  • BLOCKED

    • 定义:线程被动阻塞。

    • 触发:特指争抢 synchronized 锁失败,被挡在门外排队。


2. 安全篇:锁与原子性

2.1 悲观锁:synchronized

  • 原理:基于 Monitor(监视器锁)。

  • 锁的范围

    • synchronized(this) / 实例方法 $\rightarrow$ 锁当前对象

    • synchronized(Class) / 静态方法 $\rightarrow$ 锁类对象(全局互斥)。

  • 特点:竞争失败的线程会挂起(Block),让出 CPU。适合写多读少、竞争激烈的场景。

2.2 可见性:volatile

  • 作用:禁止 CPU 缓存优化,保证变量修改后立即写回主内存,对其他线程可见。

  • 局限:✅ 保证可见性,❌ 不保证原子性(无法解决 count++ 问题)。

2.3 乐观锁:CAS (Compare-And-Swap)

  • 实现AtomicInteger 等原子类。

  • 原理:无锁编程。只有当内存值符合预期时才修改。

  • 缺点自旋 (Spinning)。如果竞争太激烈,线程会一直 while 循环重试,导致 CPU 飙升。适合读多写少、竞争较小的场景。


3. 实战篇:线程池 (Thread Pool)

3.1 核心参数 (银行模型)

  1. corePoolSize:常驻核心线程。

  2. workQueue:阻塞队列(候客区)。

  3. maximumPoolSize:最大线程数(含临时工)。

  4. handler:拒绝策略。

3.2 拒绝策略 (4种)

当队列满且线程数达到最大时触发:

  1. AbortPolicy:直接抛异常(默认)。

  2. CallerRunsPolicy谁提交谁执行(主线程自己干)。✅ 推荐,能起到反压/限流作用。

  3. DiscardPolicy:默默丢弃,不通知。

  4. DiscardOldestPolicy:喜新厌旧,踢掉队列里排队最久的任务,接纳新任务。

3.3 生产大忌 (OOM 风险)

  • 🚫 严禁使用Executors.newFixedThreadPool()

  • 原因:它使用 LinkedBlockingQueue 且默认容量为 Integer.MAX_VALUE(无界)。

  • 后果:请求堆积导致内存耗尽,抛出 OOM (OutOfMemoryError)


4. 架构篇:分布式锁 (Redis)

场景:多台服务器(集群)抢占同一资源(如秒杀),JVM 锁失效。

4.1 演进三步曲

  1. 防死锁:必须设置过期时间 (TTL)。

    • 命令:SET key value NX EX 10 (保证原子性)。

  2. 防误删:A 任务太慢,锁过期被 B 抢走;A 完事后误删了 B 的锁。

    • 解法:Value 存入 UUID。删除前判断 if (value == myUUID) (需用 Lua 脚本保证原子性)。

  3. 业务超时 (看门狗 Watchdog)

    • 问题:业务没跑完锁就过期了。

    • 解法:使用 Redisson。后台有线程定时检查,若业务还在运行,自动续期


5. 🎯 面试 Q&A 核心复盘

Q1: 为什么要用 Runnable 而不是继承 Thread?

  • 核心点:Java 是单继承的。使用 Runnable 接口更灵活,且符合“任务与执行分离”的解耦原则。

Q2: 线程的 WAITING 和 BLOCKED 状态有什么本质区别?

  • 修正点

    • WAITING:是自愿的(如调用 wait()),线程进入等待队列,不占用 CPU(不是忙等)。

    • BLOCKED:是被迫的,因为抢不到 synchronized 锁而被挂起。

    • 误区提醒:不要把 WAITING 说成是“忙等”或“自旋”,那是 CAS 的行为。

Q3: volatile 能保证线程安全吗?

  • 核心点不能。它只能保证可见性,不能保证原子性。多线程并发写依然会出现竞态条件。

Q4: 线程池队列满了会发生什么?(OOM 问题)

  • 核心点:如果是无界队列(newFixedThreadPool),会无限制接收任务直到 内存爆满 (OOM)。如果是有界队列,满了之后会尝试创建非核心线程,如果还满则触发拒绝策略

Q5: 高并发抢购,选 Synchronized 还是 CAS?

  • 核心点:选 Synchronized (悲观锁)

  • 理由修正

    • CAS (乐观锁) 在高竞争下会产生大量自旋 (Spinning),也就是空转循环,严重浪费 CPU 资源。

    • Synchronized 在高竞争下会升级为重量级锁,让抢不到锁的线程阻塞 (Block/Sleep),挂起时不消耗 CPU,反而更高效。

Q6: Redis 分布式锁如果不加“看门狗”会有什么后果?

  • 核心点锁提前过期 + 误删锁

    • 业务没跑完锁就没了 -> 导致并发超卖(锁失效)。

    • 前一个线程结束去删锁 -> 删掉了后一个线程刚抢到的锁(连锁反应)。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐