跳到主要内容

AQS 抽象队列同步器

AQS 全称是 AbstractQueueSynchronizer,抽象队列同步器,是一个抽象类,定义了一套多线程访问共享资源的同步器框架。AQS 定义了两种资源共享方式,分别是独占共享

独占模式:只能有一个线程占有资源锁,其他竞争资源的线程在竞争失败后都会进入到等待队列中,等待占用资源的线程释放锁,然后重新被唤醒竞争资源。

- ReentrantLock ,默认非公平锁(通过构造参数可以设置为公平锁)。(在获取锁的时候不排队,直接尝试获取锁(CAS 方式),获取锁失败之后才排队)
- 内部有一个 Sync ,继承了 AQS 类,Sync 有 2 个子类
- FairSync
- NonFailSync

共享模式:允许多个线程同时获取锁,并发的访问共享资源。

  • CountDownLatch:一个计数器,用来控制多个线程同时执行。

AQS 的实现细节

- 使用了模板设计模式
- AQS 维护了一个 `volatile` 修饰的 `state` 变量和一个双向链表(FIFO队列,CLH 队列的变种)。
- state 用于判断资源是否已被线程加锁
- state = 0,表示资源未被占用
- state > 0,表示资源已被占用
- state 的值表示资源被占用的次数
- 通过 CAS 对 state 进行修改
- 双向链表用于存储等待获取资源的线程
- head 节点表示获取锁成功的节点,当头结点在释放同步状态时,会唤醒后继节点,如果后继节点获得锁成功,会把自己设置为头结点。即前驱节点是 head 节点并且在 head 节点在释放锁的同时唤醒当前节点,此时该节点才有机会获取锁。
- 每个节点包含了一个 Thread 引用,用于存储获竞争资源的线程
- 每个节点包含了一个 waitStatus 字段,用于表示节点的状态
- CANCELLED:表示节点已取消(不会被立即删除)
- 有其他线程变量队列时
- 显示的执行 release 方法时
- SIGNAL:表示当前节点的后继节点需要被唤醒。
- CONDITION:表示节点在等待队列中,等待唤醒
- PROPAGATE:表示释放共享资源时,需要唤醒后继节点

在独占模式下的非公平锁,多线程竞争资源加锁的过程

  1. 尝试获取资源,如果成功则直接返回 (有 2 次机会直接加锁)
  2. 加入等待队列的队尾,修改求取节点的 waitStatus 字段 (AQS是通过将每条请求共享资源的线程封装成一个节点来实现锁的分配)
  3. 在队列中阻塞等待,直到获取锁。自旋等待获取锁。(如果获取不到则阻塞节点中对应的线程,而被阻塞后的唤醒只能依靠前驱节点出队或者阻塞线程被中断来实现。)

如何实现一个 AQS ?

在 MyAqs 中,定义一个静态内部类 Sync ,这个 Sync 继承 AQS,重写 tryAcquire() 和 tryRelease() 方法,这两个方法分别是独占模式下获取和释放资源的方法。