双队列
在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。一个用于存储资源竞争者,一个用于标识资源的可用状态。目前理解还很肤浅,如有错误,望不吝赐教。