文章目录
一、ReentrantLock 条件变量实现原理
- 每个条件变量其实就对应着一个等待队列,其实现类是AQS中的内部类ConditionObject
1. await 流程
(1)addConditionWaiter
- 开始 Thread-0 持有锁,调用 ConditionObject 中的 await 方法,进入 ConditionObject 的 addConditionWaiter 流程
- 创建新的 Node 状态为 -2(Node.CONDITION),关联 Thread-0,加入 ConditionObject 队列尾部
(2)fullyRelease
- 进入 AQS 的 fullyRelease 流程,释放同步器上的锁(即state置为0,exclusiveOwnerThread置为null)
- 唤醒AQS队列 head 的后继节点,让后继节点去竞争锁
(3)另一线程竞争锁成功
- head的后继节点中的Thread-1加锁成功,把之前的head节点断开,等待被GC,当前节点作为head并将thread置为null,当做哨兵节点
(4)当前线程在 ConditionObject 的等待队列中 park
- 调用await的线程最终进入ConditionObject 的等待队列中park住
// 通过ReentrantLock创建条件变量对象
public class ReentrantLock implements Lock {
public Condition newCondition() {
// 调用同步器中的newCondition()
return sync.newCondition();
}
abstract static class Sync extends AbstractQueuedSynchronizer {
final ConditionObject newCondition() {
// 最终通过AQS创建出条件变量对象
return new ConditionObject();
}
}
}
public abstract class AbstractQueuedSynchronizer{
// AQS的内部类ConditionObject
public class ConditionObject implements Condition{
// 等待队列的头节点
private transient Node firstWaiter;
// 等待队列的尾节点
private transient Node lastWaiter;
public ConditionObject() { }
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 将要await的节点添加到ConditionObject的等待队列中
Node node = addConditionWaiter();
// 释放节点持有的锁
int savedState = fullyRelease(node);
int interruptMode = 0;
// 如果该节点还没有转移至 AQS 队列, 阻塞
while (!isOnSyncQueue(node)) {
// 在等待队列中 park 阻塞
LockSupport.park(this);
// 如果被打断, 退出等待队列
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 退出等待队列后, 还需要获得 AQS 队列的锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 所有已取消的 Node 从队列链表删除
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
// 应用打断模式
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
// 将所有已取消的 Node 从队列链表删除
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
// 创建一个关联当前线程的新 Node, waitStatus = -2,添加到队列尾部
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
}
// 因为线程可能重入,需要将state减为0
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
public final boolean release(int arg) {
// tryRelease释放锁,即state置为0,exclusiveOwnerThread置为null
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// 唤醒AQS同步队列head的后继节点,让后继节点去竞争锁
unparkSuccessor(h);
return true;
}
return false;
}
}
2. signal 流程
(1)唤醒 ConditionObject 中的第一个节点
- 假设 Thread-1 要来唤醒 Thread-0
- 进入 ConditionObject 的 doSignal 流程,获取等待队列中第一个 Node,即 Thread-0 所在 Node
(2)将 ConditionObject 队列中的 Node 转移到 AQS 队列中
- 执行 transferForSignal 流程,将该 Node 加入 AQS 队列尾部,将 Thread-0 的 waitStatus 改为 0,Thread-3 的waitStatus 改为 -1,ConditionObject 队列中的节点置为 null
public abstract class AbstractQueuedSynchronizer{
// AQS的内部类ConditionObject
public class ConditionObject implements Condition{
public final void signal() {
// 必须持有锁才能去唤醒其他线程, 因此 doSignal 内无需考虑加锁
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
// 等待队列不为null,进入doSignal唤醒
doSignal(first);
}
private void doSignal(Node first) {
/**
* “ConditionObject等待队列”和“AQS同步队列”中获取节点的后继节点区别:
* 在同步队列中,获取后继节点采用的是next属性。
* 在等待队列中,获取后继节点采用的是nextWaiter属性。
*/
do {
// 将ConditionObject等待队列中不是CANCELLED状态的第一个节点转移至 AQS 队列,并将ConditionObject等待队列中第一个节点的nextWaiter置为null
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
}
final boolean transferForSignal(Node node) {
// 如果状态已经不是 Node.CONDITION, 说明被取消了,返回 false 表示转移失败, 否则转移成功
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 将等待队列中的节点转移到 AQS 队列尾部
Node p = enq(node);
// 获取AQS同步队列尾部的前驱节点的waitStatus
int ws = p.waitStatus;
// 如果前驱节点是取消状态,或者状态不能置为-1,则直接将当前线程unpark
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
// 返回true,表示将ConditionObject等待队列的线程唤醒,后续唤醒后的线程自己去竞争锁
return true;
}
}