AbstractQueuedSynchronizer (AQS) -- JDK的并发框架

220 阅读2分钟

双队列

在AQS中,存在两个队列

AQS两队列

  • 等待队列:用于挂起当前线程,等待某个条件满足后唤醒或是被中断。
  • 同步队列:多线程竞争锁时,如果存在竞态,则放入同步队列,等待唤醒重新竞争。

等待队列维护在 AQS.ConditionObject 中,而同步队列维护在AQS中。 等待队列中的元素被唤醒后,会被移至同步队列,待重新竞争获得锁后,线程才能继续执行。示意代码如下

ReentrantLock lock = new ReentrantLock();
Conditon condition = lock.newCondition();
// thread 1
void run() {
    try {
      lock.lock();
      condition.await(); // 执行后,当前线程会被加入等待队列,等待signal信号或是被中断
    } catch (Exception e) {
    } finally {
      lock.unlock();
    }
}

// thread 2
void run() {
    try {
      lock.lock();
      condition.signal();  // 执行后,等待队列中的第一个线程会被唤醒,并从等待队列中移除,添加至同步队列
    } catch (Exception e) {
    } finally {
      lock.unlock();
    }
}

调用路线是 Condition::signal -> Condition::doSignal -> AQS::transferForSignal 其中调用了 AQS::enq 将元素推至同步队列,而Condition::doSignal 中,唤醒的线程会从等待队列中移除。

Node

AQS的内部类 Node是核心数据结构。等待队列和同步队列都是由其实现,可以简化为

class Node {
  Node nextWaiter; // 值有两种可能 SHARED, EXCLUSIVE
  int waitStatus;  // CANCELLED  SIGNAL  CONDITION  PROPAGATE
  Node prev; // 双向链表,指向前一个元素
  Node next; // 双向链表,指向下一个元素
}

state

State在AQS极具扩展性,在不同的实现中,state作为一个int代表了多种状态

  • 在 CountDownLatch 中,state 表示剩余的计数个数。为0时,结束。
  • 在 Semaphore 中,state 表示剩余许可的个数。
  • 在 ReentrantLock 中,state 表示锁的占用状态,0为可抢占,大于0则不可抢占,且表示了重入的次数。
  • 在 ReentrantReadWriteLock 中,state 低16位表示写锁,高16位表示读锁。同步队列中存放着竞争读锁的线程。

开放的接口

我以为,AQS的扩展性在于其同步队列和State。一个用于存储资源竞争者,一个用于标识资源的可用状态。目前理解还很肤浅,如有错误,望不吝赐教。