这是我参与8月更文挑战的第20天,活动详情查看:8月更文挑战
队列及state
AQS的队列操作
入队
- 当线程获取锁失败以后,那么这个线程就会被转换为Node节点
- 然后使用
enq(final Node node)将该节点插入到AQS的阻塞队列中
private Node enq(final Node node) {
for (;;) {
// 节点指向尾部节点
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
// 使用CAS设置一个烧饼节点为头结点
tail = head;
} else {
// 设置node的前驱节点为尾部节点
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
- 在第一次循环的时候t为
null,t指向了tail和head
- 当第二次循环的时候,利用cas去设置node节点为尾结点
- 此时便成为了双线链表的插入
state把变量
- 重点放在
await()和signal()await - await方法当被调用的时候,背部会构造一个
Node.CONDITION的node节点 - 然后把该节点插入到条件队列末尾,再释放获取的锁,再阻塞被挂起
- 创建新的node节点,并且插入到条件队列末尾
- 释放当前的线程获取的锁(看到release要注意📢)
- 调用
park方法阻塞挂起当前线程
当被挂起之后如何唤醒呢 signal
- 1是将条件队列头元素移动到AQS队列
- 下面来看await中放入条件队列
- 创建一个类型为
Node.CONDITION的节点,然后通过下面代码进行在单项队列尾部插入一个元素
- 要是多个线程一起调用
lock.lock(),只有一个线程能获得锁,其他的就会转换为Node节点 - 插入lock锁对应的AQS队列里面,并且使用CAS自旋尝试获取锁
- 如果获取到锁🔐的线程又调用了await方法,那么该线程就会释放获取到的锁,并且转换为Node节点插入到条件变量对应的条件队列里面(可以理解为「自投罗网」)
总结
一个锁对应一个AQS阻塞队列,对应多个条件变量,每个条件变量又有自己的一个条件队列✅