题目:什么是 AQS 的独占模式和共享模式?
难度:low(题库标注)
标签:java/juc, difficulty/low
评分:⭐⭐⭐☆☆(3/5)
AQS 的独占模式跟共享模式,其实有点类似于读锁跟写锁,MySQL 的读锁跟写锁独占就是正常的那个获取锁,进入线,进入一个线程,然后持有锁,然后其他线程等待。共享就是类似于信号量这种组件,它多个线程去更新对应的数量,更新到一定数量,多个线程都会更新,所以是共享的,大家更新完更新数量之后,达到一定量级才会执行下一步。
优点:
不足:
state 字段、CLH 双向链表、tryAcquire vs tryAcquireShared 两个模板方法的差别——这才是"独占 vs 共享"的机制分界点补充(必背):
独占模式 vs 共享模式对照表:
| 维度 | 独占模式 | 共享模式 |
|---|---|---|
| 允许进入的线程数 | 1 个 | 多个 |
| 模板方法 | tryAcquire(int) | tryAcquireShared(int) |
state 语义 | 0=空闲,非0=持有次数 | 允许"部分消耗",多个线程共享 state |
| 释放方法 | tryRelease(int) | tryReleaseShared(int) |
| 返回语义 | 独占获得/释放(boolean) | 共享获得/释放(返回剩余量) |
典型组件清单:
| 组件 | 模式 | 说明 |
|---|---|---|
ReentrantLock | 独占 | 独占锁 |
ReentrantReadWriteLock.WriteLock | 独占 | 写锁独占 |
ReentrantReadWriteLock.ReadLock | 共享 | 读锁共享 |
Semaphore | 共享 | 多个线程共享许可 |
CountDownLatch | 共享 | 多个线程等待计数到 0 |
CyclicBarrier | 共享(部分) | 内部用了 AQS 共享语义 |
Phaser | 共享 | 多阶段同步 |
AQS 核心结构(必背):
volatile int state:同步状态,语义由子类决定Node 类:CLH 双向链表的节点,存放等待线程、等待状态head / tail:队列的头部和尾部tryAcquire / tryRelease(独占);tryAcquireShared / tryReleaseShared(共享)——子类实现这 4 个方法之一对就完成模式选择面试杀手锏:
state:p=3 意味着 3 个线程可同时进入,是共享模式最直观的例子核心考点定位:考察对 AQS 模板方法设计的理解,尤其"独占"和"共享"两种模式在实现层面的差异,以及它们如何支撑上层各种同步组件。
知识点拆解:
tryAcquire/tryAcquireShared 去实现。独占模式返回 boolean(0/1 二选一),共享模式返回 int(允许多个线程同时进入),语义差异决定了整个实现路径不同state 存许可数考察方向:
LockSupport.park/unpark 的唤醒机制、独占锁的公平/非公平实现差异StampedLock 的读无锁化对比;与 synchronized 的 JIT 优化对比延伸递进面试题:
ReentrantReadWriteLock 就是双 AQS 实例组合——WriteLock 用独占、ReadLock 用共享tryAcquireShared 的返回值(int)而不是 boolean?state 存倒数次数,多个线程同时 await() 都能进入等待队列;只有当 state 减到 0 时才唤醒所有等待者面试官您好,AQS 是 Java 并发包的基础框架,本质是一个模板方法:它把"如何排队、如何阻塞、如何唤醒"这套骨架固定下来,让子类只实现"如何判断能否获得同步状态"这个差异点。核心数据结构是两个——一个是 volatile int state 表示同步状态,语义由子类决定;另一个是 CLH 双向链表存等待线程。
独占模式和共享模式的核心区别只有一个:能不能同时允许多个线程进入临界区。独占模式同一时刻只允许一个线程持有,state 的语义是"0 空闲、非0 持有次数",模板方法是 tryAcquire 和 tryRelease,返回 boolean 表示拿到/没拿到。典型组件是 ReentrantLock 和 ReentrantReadWriteLock.WriteLock。
共享模式允许多个线程同时进入,state 的语义是"还剩多少许可",模板方法是 tryAcquireShared 和 tryReleaseShared,返回 int 表示剩余量以便下一个线程继续。典型组件是 ReentrantReadWriteLock.ReadLock、Semaphore、CountDownLatch——比如 Semaphore 的 permits 数直接存在 state 里,p=3 就允许 3 个线程同时进入。
ReentrantReadWriteLock 其实是"独占+共享"的组合:两个独立的 AQS 实例,写锁用独占模式、读锁用共享模式。面试里如果把 MySQL 的读写锁类比为 AQS 独占/共享,会不严谨——MySQL 读写锁本身就是"混合模式",等价于 Java 的 ReadWriteLock。
选型上,如果临界区必须串行用独占模式,追求并发度用共享模式,读多写少用 ReadWriteLock。