Java基础之JUC与JMM

要理解 Java 并发编程,必须掌握JMM(Java Memory Model,Java 内存模型)JUC(java.util.concurrent,并发工具包) 这两个核心概念。JMM 是底层规范,定义了多线程交互的规则;JUC 是基于 JMM 实现的高层工具,简化了并发编程。下面分别详解:

一、JMM(Java Memory Model):多线程通信的底层规范

JMM 是抽象的内存模型 ,它并不直接对应物理硬件(如 CPU 缓存、主内存),而是定义了一套规则,用于规范线程如何通过内存进行交互,解决多线程环境下可见性、有序性、原子性问题。

1. JMM 的核心目标

多线程并发时,线程间的通信(数据共享)依赖于内存。JMM 的目标是:

  • 定义线程和主内存之间的交互方式(线程如何读取 / 写入内存);
  • 解决因 CPU 缓存、指令重排序等硬件 / 编译器优化导致的多线程数据不一致问题。

2. JMM 的核心结构

JMM 抽象了 "主内存" 和 "线程工作内存" 两个概念:

  • 主内存:所有线程共享的内存区域,存储共享变量(实例字段、静态字段等)。

  • 线程工作内存:每个线程私有的内存区域,存储主内存中共享变量的副本。线程对变量的读写操作,需先将变量从主内存加载到工作内存,修改后再同步回主内存。

(注意:这是逻辑抽象,并非物理上的内存划分,实际对应 CPU 缓存、寄存器等硬件)

3. JMM 解决的三大问题

多线程并发的核心问题是可见性、有序性、原子性,JMM 通过规则和关键字保证这些特性:

(1)可见性(Visibility)

问题 :线程 A 修改了共享变量,线程 B 可能无法立即看到最新值(因工作内存未同步到主内存)。
解决

  • volatile:强制变量读写直接操作主内存,通过 CPU 缓存一致性协议(如 MESI)通知其他线程缓存失效。
  • synchronized/Lock:解锁前必须将变量同步回主内存,加锁时必须从主内存加载最新值。
  • final:被final修饰的变量初始化后不可修改,一旦初始化完成,其他线程可见。
(2)有序性(Ordering)

问题 :编译器或 CPU 为优化性能,可能对指令重排序(单线程不影响结果,但多线程可能出错)。
解决

  • volatile:通过插入内存屏障(Memory Barrier)禁止重排序(写后加 "Store 屏障",读前加 "Load 屏障")。
  • synchronized/Lock:同步块内的指令视为一个整体,不会与块外指令重排序。
  • Happens-Before 规则 :JMM 定义的天然有序性规则,无需显式同步(如 "程序顺序规则":单线程内代码按顺序执行;"volatile 规则":volatile写操作先于读操作)。
(3)原子性(Atomicity)

问题 :一个操作(如i++)若包含多个步骤(读 - 改 - 写),多线程并发时可能被打断,导致结果错误。
解决

  • synchronized/Lock:通过排他锁保证同步块内操作的原子性(同一时间只有一个线程执行)。
  • 原子类(如AtomicInteger):基于 CPU 的 CAS(Compare-And-Swap)指令,实现无锁原子操作。

4. 关键:Happens-Before 规则

JMM 通过 "Happens-Before" 规则定义两个操作的执行顺序,无需显式同步即可保证有序性:

  • 程序顺序规则:单线程内,前面的操作 Happens-Before 后面的操作。
  • volatile 规则:volatile变量的写操作 Happens-Before 后续的读操作。
  • 锁规则:解锁操作 Happens-Before 后续的加锁操作。
  • 线程启动规则:Thread.start()Happens-Before 线程内的所有操作。
  • 线程终止规则:线程内的所有操作 Happens-Before 其他线程检测到该线程终止(如Thread.join()返回)。

二、JUC(java.util.concurrent):并发编程工具包

JUC 是 Java 5 引入的并发工具类库,基于 JMM 的规则实现,封装了复杂的并发逻辑,简化了多线程编程。核心组件包括:

1. 线程池(Executor 框架)

线程的创建 / 销毁成本高,线程池通过复用线程提高性能,核心类:

  • ExecutorService:线程池接口,定义了提交任务的方法(如submit())。
  • ThreadPoolExecutor:线程池核心实现类,可自定义核心线程数、最大线程数、队列等参数。
  • 工具类Executors:提供快捷创建线程池的方法(如newFixedThreadPool()newCachedThreadPool()),但实际开发中建议手动创建ThreadPoolExecutor以避免资源耗尽风险。

2. 并发集合(线程安全的集合)

解决HashMapArrayList等线程不安全集合的并发问题:

  • ConcurrentHashMap:线程安全的HashMap,Java 8 后基于 CAS+synchronized 实现,支持高并发读写。
  • CopyOnWriteArrayList:读操作无锁,写操作复制一份新数组修改后替换旧数组,适合读多写少场景。
  • ConcurrentLinkedQueue:无锁的并发队列,基于 CAS 实现,高效支持多线程入队 / 出队。
  • BlockingQueue:阻塞队列(如ArrayBlockingQueueLinkedBlockingQueue),常用于生产者 - 消费者模型(队列为空时读阻塞,满时写阻塞)。

3. 同步工具类

用于协调多线程的执行顺序:

  • CountDownLatch:倒计时门闩,让主线程等待多个子线程完成后再执行(countDown()减计数,await()阻塞等待计数为 0)。
  • CyclicBarrier:循环屏障,让多个线程到达屏障点后再一起继续执行(可重复使用,适合多轮协作)。
  • Semaphore:信号量,限制同时访问资源的线程数(acquire()获取许可,release()释放许可)。
  • Phaser:阶段同步器,支持动态调整参与线程数,适合分阶段的任务协作。

4. 原子操作类(java.util.concurrent.atomic)

基于 CAS 实现无锁原子操作,避免synchronized的性能开销:

  • 基本类型:AtomicIntegerAtomicLongAtomicBoolean(支持getAndIncrement()等原子操作)。
  • 引用类型:AtomicReference(原子更新对象引用)。
  • 数组类型:AtomicIntegerArray(原子更新数组元素)。
  • 字段更新器:AtomicIntegerFieldUpdater(原子更新对象的某个字段,需字段为volatile)。

5. 锁框架(java.util.concurrent.locks)

补充synchronized的不足,提供更灵活的锁机制:

  • Lock接口:定义锁的基本操作(lock()unlock()tryLock()等),实现类有:

    • ReentrantLock:可重入锁(支持公平锁 / 非公平锁,synchronized是隐式可重入)。
    • ReentrantReadWriteLock:读写锁,允许多个读线程并发,写线程独占(读多写少场景优化性能)。
  • Condition:与Lock配合使用,实现类似Object.wait()/notify()的等待 - 通知机制,支持多条件队列。

6. 其他工具

  • ThreadLocalRandom:线程安全的随机数生成器,比Random性能更高。
  • Fork/Join框架:基于分治思想的并行计算框架(ForkJoinPoolRecursiveTask),适合处理大任务拆分后的并行计算。

三、JMM 与 JUC 的关系

  • JMM 是基础:定义了多线程内存交互的规则(可见性、有序性、原子性),是所有并发机制的理论依据。

  • JUC 是实现 :JUC 中的工具类(如ReentrantLockAtomicInteger)均基于 JMM 的规则实现,例如:

    • volatile依赖 JMM 的可见性和有序性保证;
    • ReentrantLock通过 AQS(AbstractQueuedSynchronizer)框架,结合 JMM 的内存屏障和 CAS 操作实现同步;
    • 并发集合通过synchronizedLock或 CAS 保证线程安全,底层依赖 JMM 的原子性和可见性规则。

总结

  • JMM:解决 "多线程如何安全通信" 的底层规范,核心是通过规则保证可见性、有序性、原子性,是并发编程的 "宪法"。

  • JUC:基于 JMM 实现的 "工具箱",封装了线程池、并发集合、锁等工具,让开发者无需深入底层即可写出安全高效的并发代码。

掌握两者的关系,能帮助你从本质理解 Java 并发机制,避免 "只会用 API,不懂原理" 的困境。

相关推荐
xuxie131 小时前
SpringBoot文件下载(多文件以zip形式,单文件格式不变)
java·spring boot·后端
重生成为编程大王1 小时前
Java中的多态有什么用?
java·后端
666和7771 小时前
Struts2 工作总结
java·数据库
中草药z1 小时前
【Stream API】高效简化集合处理
java·前端·javascript·stream·parallelstream·并行流
野犬寒鸦2 小时前
力扣hot100:搜索二维矩阵 II(常见误区与高效解法详解)(240)
java·数据结构·算法·leetcode·面试
zru_96022 小时前
centos 系统如何安装open jdk 8
java·linux·centos
LiRuiJie2 小时前
深入剖析Spring Boot / Spring 应用中可自定义的扩展点
java·spring boot·spring
爬虫程序猿2 小时前
利用 Java 爬虫获取淘宝商品 SKU 详细信息实战指南
java·开发语言·爬虫