《Java 100 天进阶之路》并发编程与JUC详解(第51~60篇)核心知识点汇总

📅 2026/7/14 23:39:02 👁️ 阅读次数 📝 编程学习
《Java 100 天进阶之路》并发编程与JUC详解(第51~60篇)核心知识点汇总

并发编程与JUC详解(第51~60篇)核心知识点汇总

本阶段共10篇文章,覆盖Java并发编程从基础到精通的完整链路,是面试权重最高、区分度最大的模块之一。

第51篇:线程生命周期与创建方式

📖 查看完整文章

6种线程状态(Thread.State)

  • NEW:线程创建但未启动(new Thread()
  • RUNNABLE:可运行状态(就绪 + 运行中),JVM将两者合并
  • BLOCKED:阻塞状态,等待获取监视器锁(synchronized锁被占用)
  • WAITING:无限期等待,需显式唤醒(wait()join()park()
  • TIMED_WAITING:有限期等待,超时自动唤醒(sleep(ms)wait(ms)join(ms)
  • TERMINATED:终止状态(run()结束或异常退出)

4种线程创建方式

  • 继承Thread类(不推荐,单继承限制)
  • 实现Runnable接口(最常用,Lambda写法)
  • 实现Callable接口(有返回值,可抛异常)
  • 线程池(ExecutorService)

核心对比

  • sleep() vs wait():sleep不释放锁,wait释放锁;sleep时间到自醒,wait需notify唤醒
  • start() vs run():start启动新线程,run是普通方法调用
  • 虚拟线程(JDK 21+):轻量级线程(几百字节),M:N调度,适合高并发I/O密集型,不适合CPU密集型

第52篇:synchronized深度解析

📖 查看完整文章

三种使用方式

  • 修饰实例方法 → 锁当前实例this
  • 修饰静态方法 → 锁当前类的Class对象
  • 修饰代码块 → 锁括号内指定对象(推荐,粒度最细)

锁升级全流程(JDK 1.6+)

  • 无锁偏向锁轻量级锁重量级锁(单向升级,不可逆)
  • 偏向锁:单线程场景,Mark Word存线程ID,无CAS开销
  • 轻量级锁:轻度竞争,自旋等待(CAS),Lock Record在栈帧
  • 重量级锁:重度竞争,OS互斥量,ObjectMonitor(_owner_count_EntryList_WaitSet

Mark Word(64位JVM)

  • 存储锁状态、hashCode、分代年龄、偏向线程ID
  • 锁标志位:01(无锁/偏向)、00(轻量级)、10(重量级)、11(GC标记)

JVM优化

  • 自适应自旋锁(JVM动态调整自旋次数)
  • 锁消除(逃逸分析移除无竞争锁)
  • 锁粗化(合并连续加锁/解锁)

关键陷阱

  • JDK 15+默认禁用偏向锁(-XX:+UseBiasedLocking可重新启用)
  • 虚拟线程中慎用synchronized → 导致Pinning(固定在载体线程)

第53篇:volatile与JMM

📖 查看完整文章

JMM(Java内存模型)核心

  • 抽象结构:主内存(共享变量)+ 工作内存(CPU缓存)
  • JMM ≠ JVM内存结构——JMM管“怎么看见”,JVM内存结构管“存哪里”
  • 三大特性:原子性(synchronized/锁)、可见性(volatile)、有序性(内存屏障)

volatile特性

  • ✅ 保证可见性(写即刷主存,读即强制重载)
  • ✅ 禁止指令重排序(内存屏障)
  • ❌ 不保证原子性(i++是读-改-写三步)

内存屏障四种类型

  • StoreStore:禁止普通写重排到volatile写之前
  • StoreLoad:禁止volatile写重排到后续读写之后(开销最大)
  • LoadLoad:禁止volatile读后续普通读重排到前面
  • LoadStore:禁止volatile读后续普通写重排到前面

Happens-Before规则

  • volatile写 → volatile读(核心规则)
  • 程序顺序规则、锁规则、传递性

DCL单例必须加volatile:防止new Singleton()三步(分配内存→初始化→引用赋值)重排导致半初始化对象暴露 → NPE

第54篇:AQS抽象队列同步器

📖 查看完整文章

AQS核心三要素

  1. volatile int state:同步状态(0=空闲,>0=占用/重入/许可数)
  2. CLH双向FIFO队列:存储等待线程(Node节点,含thread、waitStatus、prev、next)
  3. 模板方法模式:子类实现tryAcquire/tryRelease等,AQS提供acquire/release骨架

两种同步模式

  • 独占模式(Exclusive):同一时刻只有一个线程能获取 → ReentrantLock
  • 共享模式(Shared):同一时刻多个线程可获取 → Semaphore、CountDownLatch

Node.waitStatus:CANCELLED(1)、SIGNAL(-1)、CONDITION(-2)、PROPAGATE(-3)、0(初始)

ReentrantLock可重入实现:state计数,当前线程是持有者时state++,解锁时state--归零释放

第55篇:线程池ThreadPoolExecutor

📖 查看完整文章

七大核心参数

参数说明
corePoolSize核心线程数(常驻员工)
maximumPoolSize最大线程数(核心+非核心)
keepAliveTime非核心线程空闲存活时间
unit时间单位
workQueue工作队列(BlockingQueue)
threadFactory线程工厂(自定义线程名)
handler拒绝策略

四步执行流程

  1. 当前线程数 < corePoolSize → 创建核心线程执行
  2. 核心线程满 → 任务放入队列等待
  3. 队列满 → 当前线程数 < maximumPoolSize → 创建非核心线程
  4. 线程也满 → 执行拒绝策略

四种拒绝策略

  • AbortPolicy(默认):抛RejectedExecutionException
  • CallerRunsPolicy:调用者线程执行(可防止任务丢失,会阻塞调用者)
  • DiscardPolicy:静默丢弃新任务
  • DiscardOldestPolicy:丢弃队列最旧任务,重新提交

Executors陷阱(禁止使用)

  • newFixedThreadPool:无界LinkedBlockingQueue → 任务堆积 → OOM
  • newCachedThreadPool:maxPoolSize=Integer.MAX_VALUE → 无限线程 → 资源耗尽

线程数计算公式:CPU密集型 = CPU核数 + 1;IO密集型 = CPU核数 × 2

第56篇:死锁与排查工具

📖 查看完整文章

死锁四大必要条件(Coffman条件)

  • 互斥:资源一次只能被一个线程占用
  • 持有并等待:持有资源的同时等待其他资源
  • 不可剥夺:资源只能主动释放,不能强行剥夺
  • 循环等待:线程之间形成等待环路

排查工具

  • jstack -l <PID>(命令行首选):输出Found one Java-level deadlock,定位waiting to locklocked行号
  • jconsole(图形化):一键检测死锁
  • VisualVM(深度分析):线程状态监控

死锁 vs 活锁 vs 饥饿

  • 死锁:永久阻塞,不主动干预永不恢复
  • 活锁:不阻塞但不断重试失败,空转无推进
  • 饥饿:有进展但极其缓慢(低优先级线程)

预防策略(破坏四大条件之一):

  • 统一锁顺序(破坏循环等待)
  • ReentrantLock.tryLock(timeout)超时(破坏不可剥夺)
  • 一次性申请所有资源,失败则全部释放(破坏持有并等待)
  • 减少锁粒度(使用ConcurrentHashMap等)

第57篇:CompletableFuture异步编程

📖 查看完整文章

Future四大痛点

  • 阻塞获取结果(get()异步变同步)
  • 无法链式调用
  • 无法组合多个任务
  • 异常处理困难

核心API

方法说明
supplyAsync(Supplier)有返回值的异步任务
runAsync(Runnable)无返回值的异步任务
thenApply(Function)同步转换结果(同一线程)
thenApplyAsync(Function)异步转换结果(新线程)
thenCompose(Function)串联异步任务(扁平化)
thenCombine并行合并两个任务结果
allOf等待所有任务完成(Void)
anyOf等待任意一个任务完成

异常处理三件套

  • exceptionally(Function):仅异常时触发,返回兜底值
  • handle(BiFunction):成功/失败都触发,可继续链式处理
  • whenComplete(BiConsumer):收尾工作,不改变结果,异常继续传播

线程池配置

  • 禁止使用ForkJoinPool.commonPool():业务异步必须自建线程池
  • CPU密集型:接近CPU核心数;IO密集型:CPU核心数×2

第58篇:原子类与CAS

📖 查看完整文章

CAS三要素:内存位置 V、预期值 A、新值 B

  • CPU原子指令(x86:lock cmpxchg),硬件保证原子性
  • CAS失败 → 自旋重试(for(;;)循环)

原子类五大分类

  1. 基本类型:AtomicIntegerAtomicLongAtomicBoolean
  2. 数组类型:AtomicIntegerArrayAtomicLongArrayAtomicReferenceArray
  3. 引用类型:AtomicReferenceAtomicStampedReference(解决ABA)、AtomicMarkableReference
  4. 属性更新器:AtomicIntegerFieldUpdater等(零内存开销,字段必须volatile)
  5. 高性能累加器:LongAdderDoubleAdderLongAccumulator

ABA问题:值 A→B→A,CAS误判为未被修改 →AtomicStampedReference(版本号)解决

LongAdder设计

  • 低并发:直接CAS更新base
  • 高并发:分段累加Cell[](各自累加,最后求和)
  • @Contended防止伪共享(每个Cell独占缓存行)
  • 性能是AtomicLong的4倍以上

VarHandle替代Unsafe(JDK 23重要变更)

  • Unsafe内存访问方法已废弃(JEP 471)
  • 推荐方案:MethodHandles.lookup().findVarHandle()
  • 优势:直接映射JVM字节码,类型校验,精细内存顺序控制

第59篇:ThreadLocal原理与内存泄漏

📖 查看完整文章

核心设计:空间换时间,每个线程独立变量副本,实现线程间数据隔离

JDK 1.8架构(控制权反转)

  • ThreadLocal 不再维护全局Map
  • 每个Thread对象持有自己的ThreadLocalMap
  • ThreadLocal实例作为Map的key

Entry结构(关键设计)

  • key(ThreadLocal实例)WeakReference弱引用
  • value(变量副本):强引用

内存泄漏完整链路

ThreadLocal外部引用被置空 → GC回收ThreadLocal对象 → Entry.key变为null(弱引用特性)→ Entry.value仍是强引用无法回收 → 线程存活 → ThreadLocalMap存活 → null-key Entry永久残留 → 🔴内存泄漏

关键结论

  • 弱引用只解决了ThreadLocal对象的回收(缓解泄漏),不能解决value的回收
  • 根治方案:try-finally保证remove()执行

跨线程传递方案

  • InheritableThreadLocal:父子线程传递(仅新建子线程时复制)
  • TransmittableThreadLocal(阿里开源):线程池场景上下文传递

remove() vs set(null)

  • remove():删除整个Entry(key+value),彻底释放内存
  • set(null):只设value为null,Entry仍存在,内存泄漏未解决

第60篇:并发编程面试压轴题

📖 查看完整文章

本阶段共10篇,内容对应第51~59篇的全部考点,覆盖8大模块、30+高频题

核心模块速览

模块核心考点面试权重
基础概念线程6种状态、sleep/wait、4种创建方式⭐⭐⭐⭐
锁机制synchronized锁升级、Mark Word、ReentrantLock⭐⭐⭐⭐⭐
JMM与volatile可见性、有序性、内存屏障、happens-before⭐⭐⭐⭐⭐
AQS与并发工具核心三要素、独占/共享模式、Semaphore⭐⭐⭐⭐⭐
线程池七大参数、执行流程、拒绝策略、Executors陷阱⭐⭐⭐⭐⭐
死锁排查四大必要条件、jstack实战⭐⭐⭐⭐
异步与无锁CompletableFuture编排、CAS三要素、ABA⭐⭐⭐⭐
ThreadLocal内存泄漏根因、弱引用/强引用、remove⭐⭐⭐⭐