基于本文回答
0
评论

AQS 中独占模式(Exclusive)和共享模式(Share)的区别?

知识点图片

在 Java 的并发编程中,AQS(AbstractQueuedSynchronizer) 是构建锁和同步器的核心框架。AQS 内部维护了一个 volatile int state(同步状态)和一个 FIFO 的双向链表(等待队列)。

AQS 支持两种同步模式:独占模式(Exclusive)共享模式(Share)。它们的根本区别在于能否有多个线程同时获取到同步资源

以下是两者的详细对比和原理解析:


1. 核心概念对比

  • 独占模式(Exclusive):

    • 定义:同一时刻,只能有一个线程获取到同步资源。其他尝试获取资源的线程都会被阻塞并加入到等待队列中。
    • 比喻:单人洗手间。一个人进去后门就锁上了,其他人必须在外面排队,等里面的人出来才能进去下一个。
    • 典型应用ReentrantLock
  • 共享模式(Shared):

    • 定义:同一时刻,可以有多个线程同时获取到同步资源(只要资源数量允许)。
    • 比喻:有 N 个座位的阅览室。只要还有空座位,多个人可以同时进去看书;如果没有空座位了,新来的人才需要排队。
    • 典型应用Semaphore(信号量)、CountDownLatch(倒计时器)、ReentrantReadWriteLock 的读锁。

2. 唤醒机制的区别(最关键的底层差异)

在 AQS 的等待队列中,当持有锁的线程释放资源后,如何唤醒后续排队的线程,是这两种模式最大的不同:

  • 独占模式的唤醒(点对点):

    • 当独占锁释放时,它只会唤醒等待队列中紧接着的下一个节点(即头节点的后继节点)。
    • 流程:线程 A 释放锁 -> 唤醒线程 B -> 线程 B 获取锁 -> 结束。
  • 共享模式的唤醒(传播机制 Propagate):

    • 当一个线程以共享模式成功获取资源后,如果资源还有剩余(state > 0),它不仅自己会获取资源,还会主动唤醒下一个排队的共享节点。下一个节点获取成功后,如果还有剩余资源,会继续唤醒下下个节点,形成连锁反应(传播)
    • 当共享锁释放时,也会触发唤醒后续节点的操作。
    • 流程:线程 A 获取共享锁 -> 发现还有资源 -> 唤醒排在后面的线程 B -> 线程 B 获取锁 -> 发现还有资源 -> 唤醒线程 C... 直到资源耗尽。

3. 需要重写的方法不同

AQS 是基于模板方法模式设计的,自定义同步器时,需要根据使用哪种模式来重写不同的方法:

模式 需要重写的方法 返回值含义
独占模式 tryAcquire(int arg) booleantrue 表示获取成功,false 表示失败排队。
tryRelease(int arg) booleantrue 表示释放成功并可以唤醒后续节点,false 表示释放失败。
共享模式 tryAcquireShared(int arg) int
< 0:获取失败,需排队。
= 0:获取成功,但没有剩余资源,无需唤醒后续节点。
> 0:获取成功,且有剩余资源,需要唤醒后续排队节点。
tryReleaseShared(int arg) booleantrue 表示释放成功且可以唤醒后续节点。

4. 节点(Node)的标记不同

AQS 的等待队列(CLH 队列)中的节点(Node),在创建时会标记其等待的模式:

  • Node.EXCLUSIVE:独占模式节点。表示该线程正在等待独占锁。
  • Node.SHARED:共享模式节点。表示该线程正在等待共享锁。

注:一个 AQS 的队列中,是可以同时存在独占节点和共享节点的(例如 ReentrantReadWriteLock 的等待队列)。


5. 能否混合使用?

可以混合使用。 AQS 允许在一个同步器中同时实现独占模式和共享模式。

典型代表:ReentrantReadWriteLock(读写锁)

  • 写锁(WriteLock) 使用了独占模式:写和写互斥、读和写互斥。调用的是 acquire()release()
  • 读锁(ReadLock) 使用了共享模式:读和读不互斥,多个线程可以同时读取。调用的是 acquireShared()releaseShared()
  • 它巧妙地将 AQS 的一个 32 位的 state 变量拆分为两部分:高 16 位表示读锁(共享)的持有次数,低 16 位表示写锁(独占)的持有次数。

总结表

特性 独占模式 (Exclusive) 共享模式 (Shared)
资源访问 仅限单一线程 允许多个线程同时访问
状态(state)含义 通常 0 为可用,1 为被占用(重入时递增) 通常表示可用资源的数量或屏障的状态
获取资源方法 acquire() / tryAcquire() acquireShared() / tryAcquireShared()
释放资源方法 release() / tryRelease() releaseShared() / tryReleaseShared()
唤醒机制 仅唤醒队列中的下一个有效节点 传播机制(Propagate),不仅自己获取,还会唤醒后续共享节点
典型实现类 ReentrantLock Semaphore, CountDownLatch
右滑查看面试常问