Java juc
JUC 是 java.util.concurrent 的缩写,它是 Java 标准库中一个至关重要的包,专门用于支持并发编程。从 Java 5 开始引入,JUC 极大地简化了多线程开发的复杂性,并提供了更强大、更灵活、更安全的并发工具。
可以毫不夸张地说,掌握 JUC 是从“会用多线程”到“精通并发编程”的必经之路。
一、 为什么需要 JUC ?
在 JUC 出现之前,我们主要依赖 synchronized、wait()、notify() 等关键字和方法来实现同步。但它们存在一些明显的缺点:
- 不够灵活:
synchronized是非公平锁,且无法中断一个正在等待获取锁的线程,也无法设置超时。 - 功能单一:它只是一个简单的互斥锁,无法实现更复杂的锁功能,如读写锁。
- API 原始:
wait()/notify()的使用非常容易出错,比如忘记在synchronized块内调用,或者信号的丢失(notify 在 wait 之前执行)。 - 性能瓶颈:在 JDK 1.6 之前,
synchronized是重量级锁,性能较差。虽然之后有了锁升级等优化,但 JUC 中的Lock在某些场景下依然有优势。
JUC 的出现,就是为了解决这些问题,提供一套更现代、更强大的并发工具集。
二、 JUC 的核心组成部分
JUC 包内容非常丰富,我们可以将其分为以下几个核心模块:
1. locks - 锁框架
这是对 synchronized 的增强和替代,提供了更灵活、更强大的锁机制。
Lock接口:是所有锁的顶层接口,定义了标准的锁操作。lock():获取锁,如果锁不可用,则线程会阻塞。unlock():释放锁(必须在finally块中调用,否则可能导致死锁)。lockInterruptibly():可中断地获取锁,允许线程在等待时响应中断。tryLock():尝试获取锁,如果立即可用则返回true,否则返回false,不会阻塞。tryLock(long time, TimeUnit unit):在给定时间内尝试获取锁。
ReentrantLock(可重入锁):Lock接口最常见的实现。- 可重入:与
synchronized一样,一个线程可以多次获取同一个锁。 - 可公平/非公平:可以在构造时指定是公平锁(按请求顺序获取)还是非公平锁(可插队,吞吐量更高)。默认是非公平锁。
- 可中断/可超时:支持
lockInterruptibly()和tryLock()。
- 可重入:与
ReentrantReadWriteLock(读写锁):一种非常高效的锁策略,适用于“读多写少”的场景。- 读锁(共享锁):多个线程可以同时持有读锁,互不影响。
- 写锁(排他锁):只有一个线程能持有写锁,且在持有期间,其他任何线程(包括读线程)都不能获取任何锁。
- 锁降级:支持从写锁降级为读锁,但不支持从读锁升级为写锁。
StampedLock(邮戳锁):Java 8 引入,是ReentrantReadWriteLock的性能增强版。- 增加了 乐观读 模式。在读操作时,先尝试乐观读(不加锁),通过返回的
stamp版本号校验数据是否被修改。如果没被修改,就直接返回,避免了读锁的开销;如果被修改,再升级为悲观读锁。性能极高。
- 增加了 乐观读 模式。在读操作时,先尝试乐观读(不加锁),通过返回的
2. atomic - 原子类
原子类保证了在单个变量上的“读-改-写”操作的原子性,底层通常利用 CAS (Compare-And-Swap) 算法实现,无锁但线程安全。
- 基本类型:
AtomicInteger,AtomicLong,AtomicBoolean - 引用类型:
AtomicReference,AtomicStampedReference(解决 CAS 的 ABA 问题),AtomicMarkableReference - 字段更新器:
AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUpdater(可以原子地更新对象的某个字段) - JDK 8 新增:
LongAdder,LongAccumulator。在高并发场景下,比AtomicLong性能更好,因为它通过分段累加来减少竞争。
3. executor - 线程池框架
这是 JUC 中最核心、最常用的部分,用于管理和复用线程,避免频繁创建和销毁线程带来的开销。
Executor:顶层接口,只定义了execute(Runnable)方法。ExecutorService:扩展了Executor,增加了生命周期管理方法(shutdown(),submit()等)。ScheduledExecutorService:扩展了ExecutorService,支持定时和周期性任务。ThreadPoolExecutor:线程池的核心实现类。构造函数非常关键,有7个参数:corePoolSize:核心线程数。maximumPoolSize:最大线程数。keepAliveTime:空闲线程存活时间。unit:时间单位。workQueue:任务队列(ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue等)。threadFactory:线程工厂。handler:拒绝策略(AbortPolicy,CallerRunsPolicy,DiscardPolicy,DiscardOldestPolicy)。
Executors工具类:提供了一系列快速创建线程池的静态方法,如newFixedThreadPool(),newCachedThreadPool(),newSingleThreadExecutor()。注意:《阿里巴巴Java开发手册》中不建议直接使用这些方法,因为它们可能隐藏了参数配置的细节,容易导致OOM。推荐手动创建ThreadPoolExecutor。
4. collections - 并发集合
提供了线程安全的集合类,是 java.util 中 Collections.synchronizedXXX() 的替代品,性能更好。
ConcurrentHashMap:线程安全的HashMap。在 JDK 1.7 中使用分段锁,JDK 1.8 及以后改为 CAS +synchronized,性能和并发度更高。CopyOnWriteArrayList:写时复制的ArrayList。读操作无锁,性能极高;写操作时会复制一份新数组,再修改,最后引用指向新数组。适用于“读多写极少”的场景,如事件监听器列表。CopyOnWriteArraySet:基于CopyOnWriteArrayList实现的线程安全的Set。ConcurrentLinkedQueue:线程安全的无界非阻塞队列。BlockingQueue(接口):阻塞队列,是线程池的核心组件。ArrayBlockingQueue:有界、数组结构、公平/非公平锁。LinkedBlockingQueue:可选有界、链表结构、锁分离(读写锁不同)。SynchronousQueue:无缓冲队列,每个put操作必须等待一个take操作,反之亦然。Executors.newCachedThreadPool()就用了它。PriorityBlockingQueue:带优先级的无界阻塞队列。
5. tools - 同步工具类
提供了三个强大的辅助类,用于协调多个线程的协作。
CountDownLatch(倒计时门闩):- 作用:让一个或多个线程等待其他一组线程完成操作后再继续执行。
- 核心方法:
countDown()(计数器减1),await()(阻塞等待计数器归零)。 - 特点:计数器只能使用一次,不能重置。
CyclicBarrier(循环栅栏):- 作用:让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,所有被阻塞的线程才会继续执行。
- 核心方法:
await()。 - 特点:可以循环使用。当所有线程越过屏障后,它可以重置以供下次使用。还可以在构造时传入一个
Runnable,在屏障触发时执行。
Semaphore(信号量):- 作用:控制同时访问特定资源的线程数量,常用于流量控制。
- 核心方法:
acquire()(获取一个许可,如果无可用许可则阻塞),release()(释放一个许可)。
三、 JUC 的底层原理
JUC 的高性能并非凭空而来,它依赖于几个关键的底层技术:
- CAS (Compare-And-Swap)
- 一种无锁算法,包含三个操作数:内存位置V、旧的预期值A、要修改的新值B。
- 当且仅当 V 的值等于 A 时,才将 V 的值更新为 B。否则什么都不做。这是一个原子操作。
- Java 中的
Unsafe类提供了 CAS 操作,原子类就是基于它实现的。 - ABA 问题:一个值从 A -> B -> A,CAS 会认为它没有变过。
AtomicStampedReference通过版本号机制解决了这个问题。
- AQS (AbstractQueuedSynchronizer)
- JUC 的灵魂!这是一个抽象的队列式同步器,是
ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch等大部分锁和同步工具类的核心构建框架。 - 核心思想:
- 使用一个
volatile int state变量来表示同步状态。 - 使用一个 CLH (Craig, Landin, and Hagersten) 队列 来管理等待获取资源的线程。
- 使用一个
- 工作模式:
- 独占模式:资源一次只能被一个线程占有(如
ReentrantLock)。 - 共享模式:资源可以被多个线程同时占有(如
Semaphore、CountDownLatch的await)。
- 独占模式:资源一次只能被一个线程占有(如
- 我们使用的各种锁和工具,本质上都是在 AQS 的基础上,实现了
tryAcquire/tryRelease(独占) 或tryAcquireShared/tryReleaseShared(共享) 等模板方法,定义了state变量的具体含义和操作逻辑。
- JUC 的灵魂!这是一个抽象的队列式同步器,是
四、 实战应用场景举例
- 高并发秒杀系统:
- 使用
AtomicInteger或LongAdder来记录当前请求数或库存数,保证原子性。 - 使用
Semaphore限制同时进入下单流程的线程数,防止数据库被冲垮。 - 使用
ThreadPoolExecutor处理下单请求,避免为每个请求创建新线程。
- 使用
- 数据缓存预热:
- 启动多个线程分别加载不同的缓存数据。
- 主线程使用
CountDownLatch等待所有加载线程完成后,再对外提供服务。
- 多阶段任务处理:
- 一个复杂任务分为多个阶段,每个阶段需要所有子线程都完成才能进入下一阶段。
- 使用
CyclicBarrier在每个阶段末尾进行同步。
五、 总结与学习建议
| 模块 | 核心类/接口 | 解决的问题 | 关键特性 |
|---|---|---|---|
locks | ReentrantLock, ReadWriteLock | 替代 synchronized,提供更灵活的锁控制 | 可重入、可中断、可超时、公平/非公平、读写分离 |
atomic | AtomicInteger, LongAdder | 保证单个变量操作的原子性 | CAS、无锁、高性能 |
executor | ThreadPoolExecutor | 线程资源管理,避免频繁创建销毁 | 复用线程、控制并发、任务队列、拒绝策略 |
collections | ConcurrentHashMap, CopyOnWriteArrayList | 提供线程安全的高性能集合 | 分段锁/CAS、写时复制 |
tools | CountDownLatch, CyclicBarrier, Semaphore | 多线程间的协调与同步 | 倒计时、循环栅栏、信号量控制 |
| 学习建议: |
- 从线程池开始:
ThreadPoolExecutor是日常开发中最常用的,先理解它的7个参数和工作流程。 - 掌握原子类:理解
CAS原理,知道何时用AtomicInteger,何时用LongAdder。 - 深入锁机制:对比
synchronized和ReentrantLock的区别,学习读写锁的应用场景。 - 理解同步工具:通过实际案例理解
CountDownLatch、CyclicBarrier和Semaphore的不同用途。 - 挑战底层原理:深入学习 AQS 的源码,这是理解 JUC 设计精髓的关键。

