条件等待队列

条件等待队列

第一节 简介

当多个线程 await() 在同一个条件变量上时,会形成一个条件等待队列。同一个锁可以创建多个条件变量,就会存在多个条件等待队列。这个队列和 AQS 的队列结构很接近,只不过它不是双向队列,而是单向队列。队列中的节点和 AQS 等待队列的节点是同一个类,但是节点指针不是 prevnext ,而是 nextWaiter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class AQS {
...
class ConditionObject {
Node firstWaiter; // 指向第一个节点
Node lastWaiter; // 指向第二个节点
}

class Node {
static final int CONDITION = -2;
static final int SIGNAL = -1;
Thread thread; // 当前等待的线程
Node nextWaiter; // 指向下一个条件等待节点

Node prev;
Node next;
int waitStatus; // waitStatus = CONDITION
}
...
}

ConditionObject 是 AQS 的内部类,这个对象里会有一个隐藏的指针 this$0 指向外部的 AQS 对象,ConditionObject 可以直接访问 AQS 对象的所有属性和方法(加锁解锁)。位于条件等待队列里的所有节点的 waitStatus 状态都被标记为 CONDITION,表示节点是因为条件变量而等待。


第二节 队列转移

当条件变量的 signal() 方法被调用时,条件等待队列的头节点线程会被唤醒,该节点从条件等待队列中被摘走,然后被转移到 AQS 的等待队列中,准备排队尝试重新获取锁。这时节点的状态从 CONDITION 转为 SIGNAL ,表示当前节点是被条件变量唤醒转移过来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class AQS {
...
boolean transferForSignal(Node node) {
// 重置节点状态
if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false
Node p = enq(node); // 进入 AQS 等待队列
int ws = p.waitStatus;
// 再修改状态为SIGNAL
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
...
}

被转移的节点的 nextWaiter 字段的含义也发生了变更,在条件队列里它是下一个节点的指针,在 AQS 等待队列里它是共享锁还是互斥锁的标志。

Java 并发包常用类库依赖结构


参考博客和文章书籍等:

《Java核心技术 卷Ⅰ》

因博客主等未标明不可引用,若部分内容涉及侵权请及时告知,我会尽快修改和删除相关内容