JUCjava.util.concurrent 的缩写,它是 Java 标准库中一个至关重要的包,专门用于支持并发编程。从 Java 5 开始引入,JUC 极大地简化了多线程开发的复杂性,并提供了更强大、更灵活、更安全的并发工具。
可以毫不夸张地说,掌握 JUC 是从“会用多线程”到“精通并发编程”的必经之路

一、 为什么需要 JUC ?

在 JUC 出现之前,我们主要依赖 synchronizedwait()notify() 等关键字和方法来实现同步。但它们存在一些明显的缺点:

  1. 不够灵活synchronized 是非公平锁,且无法中断一个正在等待获取锁的线程,也无法设置超时。
  2. 功能单一:它只是一个简单的互斥锁,无法实现更复杂的锁功能,如读写锁。
  3. API 原始wait()/notify() 的使用非常容易出错,比如忘记在 synchronized 块内调用,或者信号的丢失(notify 在 wait 之前执行)。
  4. 性能瓶颈:在 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个参数:
    1. corePoolSize:核心线程数。
    2. maximumPoolSize:最大线程数。
    3. keepAliveTime:空闲线程存活时间。
    4. unit:时间单位。
    5. workQueue:任务队列(ArrayBlockingQueue, LinkedBlockingQueue, SynchronousQueue 等)。
    6. threadFactory:线程工厂。
    7. handler:拒绝策略(AbortPolicy, CallerRunsPolicy, DiscardPolicy, DiscardOldestPolicy)。
  • Executors 工具类:提供了一系列快速创建线程池的静态方法,如 newFixedThreadPool(), newCachedThreadPool(), newSingleThreadExecutor()注意:《阿里巴巴Java开发手册》中不建议直接使用这些方法,因为它们可能隐藏了参数配置的细节,容易导致 OOM。推荐手动创建 ThreadPoolExecutor

4. collections - 并发集合

提供了线程安全的集合类,是 java.utilCollections.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 的高性能并非凭空而来,它依赖于几个关键的底层技术:

  1. CAS (Compare-And-Swap)
    • 一种无锁算法,包含三个操作数:内存位置V、旧的预期值A、要修改的新值B。
    • 当且仅当 V 的值等于 A 时,才将 V 的值更新为 B。否则什么都不做。这是一个原子操作。
    • Java 中的 Unsafe 类提供了 CAS 操作,原子类就是基于它实现的。
    • ABA 问题:一个值从 A -> B -> A,CAS 会认为它没有变过。AtomicStampedReference 通过版本号机制解决了这个问题。
  2. AQS (AbstractQueuedSynchronizer)
    • JUC 的灵魂!这是一个抽象的队列式同步器,是 ReentrantLockReentrantReadWriteLockSemaphoreCountDownLatch 等大部分锁和同步工具类的核心构建框架。
    • 核心思想
      • 使用一个 volatile int state 变量来表示同步状态。
      • 使用一个 CLH (Craig, Landin, and Hagersten) 队列 来管理等待获取资源的线程。
    • 工作模式
      • 独占模式:资源一次只能被一个线程占有(如 ReentrantLock)。
      • 共享模式:资源可以被多个线程同时占有(如 SemaphoreCountDownLatchawait)。
    • 我们使用的各种锁和工具,本质上都是在 AQS 的基础上,实现了 tryAcquire/tryRelease (独占) 或 tryAcquireShared/tryReleaseShared (共享) 等模板方法,定义了 state 变量的具体含义和操作逻辑。

四、 实战应用场景举例

  1. 高并发秒杀系统
    • 使用 AtomicIntegerLongAdder 来记录当前请求数或库存数,保证原子性。
    • 使用 Semaphore 限制同时进入下单流程的线程数,防止数据库被冲垮。
    • 使用 ThreadPoolExecutor 处理下单请求,避免为每个请求创建新线程。
  2. 数据缓存预热
    • 启动多个线程分别加载不同的缓存数据。
    • 主线程使用 CountDownLatch 等待所有加载线程完成后,再对外提供服务。
  3. 多阶段任务处理
    • 一个复杂任务分为多个阶段,每个阶段需要所有子线程都完成才能进入下一阶段。
    • 使用 CyclicBarrier 在每个阶段末尾进行同步。

五、 总结与学习建议

模块核心类/接口解决的问题关键特性
locksReentrantLock, ReadWriteLock替代 synchronized,提供更灵活的锁控制可重入、可中断、可超时、公平/非公平、读写分离
atomicAtomicInteger, LongAdder保证单个变量操作的原子性CAS、无锁、高性能
executorThreadPoolExecutor线程资源管理,避免频繁创建销毁复用线程、控制并发、任务队列、拒绝策略
collectionsConcurrentHashMap, CopyOnWriteArrayList提供线程安全的高性能集合分段锁/CAS、写时复制
toolsCountDownLatch, CyclicBarrier, Semaphore多线程间的协调与同步倒计时、循环栅栏、信号量控制
学习建议
  1. 从线程池开始ThreadPoolExecutor 是日常开发中最常用的,先理解它的7个参数和工作流程。
  2. 掌握原子类:理解 CAS 原理,知道何时用 AtomicInteger,何时用 LongAdder
  3. 深入锁机制:对比 synchronizedReentrantLock 的区别,学习读写锁的应用场景。
  4. 理解同步工具:通过实际案例理解 CountDownLatchCyclicBarrierSemaphore 的不同用途。
  5. 挑战底层原理:深入学习 AQS 的源码,这是理解 JUC 设计精髓的关键。