一、条件队列
AQS的一个锁可以对应多个条件变量。使用时先使用lock.newCondition()来new出来一个在AQS内部声明的ConditionObject对象,ConditionObject是AQS的内部类,可以访问AQS内部的变量和方法。而在每个条件变量内部都维护了一个条件队列,用来存放调用条件变量的await()方法时被阻塞的线程。
这个条件队列和AQS队列不是一样东西!
二、源码分析
2.1 ConditionObject内部类的继承关系
可以看出ConditionObject实现了Condition接口
2.2 Condition接口
public interface Condition {
/**
* 线程进入等待状态直到被通知或中断
*/
void await() throws InterruptedException;
/**
* 当线程进入等待状态直到被通知,该方法对中断不敏感
*/
void awaitUninterruptibly();
/**
* 当前线程进入等待状态直到被通知、中断或者超时(纳秒)。返回值为剩余的时间
*/
long awaitNanos(long nanosTimeout) throws InterruptedException;
/**
* 当前线程进入等待状态直到被通知、中断或者超时
*/
boolean await(long time, TimeUnit unit) throws InterruptedException;
/**
* 线程进入等待状态直到被通知、中断或者到某个时间
*/
boolean awaitUntil(Date deadline) throws InterruptedException;
/**
* 唤醒一个在Condition上的线程
*/
void signal();
/**
* 唤醒所有等待在Condition上的线程
void signalAll();
}
2.3 ConditionObject内部类
因为Conditon的操作需要获取相关联的锁,所以作为同步器AQS的内部类。每个Condition对象都包含了一个等待队列。
2.3.1 等待队列
Condition等待队列的节点Node和AQS等待队列的节点是一样的
/** 头节点. */
private transient Node firstWaiter;
/** 尾节点. */
private transient Node lastWaiter;
Condition的等待队列不像AQS那样是双链表,Condition的是单链表队列。lock可以拥有多个条件队列。
2.3.2 等待方法
public final void await() throws InterruptedException {
//线程被中断抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//将当前线程添加到条件队列尾部
Node node = addConditionWaiter();
//释放锁
int savedState = fullyRelease(node);
int interruptMode = 0;
//如果不在同步队列
while (!isOnSyncQueue(node)) {
//线程被挂起
LockSupport.park(this);
//中断操作
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//-----划重点:线程在此处醒来,以独占锁模式争夺锁------
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
//删除条件队列中被取消的节点
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.
//队尾节点部位null,等待状态不是CONDITION
if (t != null && t.waitStatus != Node.CONDITION) {
//清除节点,其实就是链表删除,不详细说了
unlinkCancelledWaiters();
//再次获取尾节点
t = lastWaiter;
}
//封装当前线程,等待状态到节点中
Node node = new Node(Thread.currentThread(), Node.CONDITION);
//如果t为null,队列为空
if (t == null)
firstWaiter = node;
else
//否则将入队尾
t.nextWaiter = node;
//队尾指向新加入的节点
lastWaiter = node;
//返回新节点
return node;
}
节点已加入条件队列,开始释放锁
//释放锁
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要子类自己实现
if (tryRelease(arg)) {
//获取阻塞队列的头节点
Node h = head;
if (h != null && h.waitStatus != 0)
//unpark后继节点
unparkSuccessor(h);
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
/*
* 传入的node节点,此时为头结点,waitStatus小于0则置0
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* 找到头节点后的第一个排队节点(等待状态<=0)
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
//让s节点的thread获取线程许可证
LockSupport.unpark(s.thread);
}
进行条件等待,传入的是当前线程
/**
* 判断节点是否在同步队列中
*/
final boolean isOnSyncQueue(Node node) {
//等待队列无prev节点
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
//等待队列的后继节点是nextWaiter
if (node.next != null) // If has successor, it must be on queue
return true;
/*
* node.prev can be non-null, but not yet on queue because
* the CAS to place it on queue can fail. So we have to
* traverse from tail to make sure it actually made it. It
* will always be near the tail in calls to this method, and
* unless the CAS failed (which is unlikely), it will be
* there, so we hardly ever traverse much.
* 上面无法判断,再去阻塞队列中判断
*/
return findNodeFromTail(node);
}
2.3.3 唤醒方法
public final void signal() {
//判断当前线程是否持有锁
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//获得第一个等待线程
Node first = firstWaiter;
//如果条件队列有排队者
if (first != null)
//唤醒条件队列的头节点
doSignal(first);
}
//唤醒条件队列的头节点
private void doSignal(Node first) {
do {
//将等待队列的头节点移动至first的后继节点,如果此时头节点为空,则条件队列也为空
if ( (firstWaiter = first.nextWaiter) == null)
//尾节点指针也置空
lastWaiter = null;
//first的nextWaiter引用置null,要拿去阻塞队列了,跟条件队列没关系了
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
/*
* 如果失败则是该节点被取消了
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
/*
* 将节点加入阻塞队列
*/
//同独占锁入队
Node p = enq(node);
//返回p的前置节点
int ws = p.waitStatus;
//如果ws > 0 被取消 或 修改状态失败则直接唤醒node节点线程
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
//获取线程许可证,获取到锁或被挂起
LockSupport.unpark(node.thread);
return true;
}
三、总结
以上是条件队列的内容。
首先要明白一个锁只有一个AQS阻塞队列,但是可以有多个条件变量,每个条件变量有自己的一个条件队列。
await()方法会使该线程放入条件变量的条件队列中,当另外线程调用条件变量的signal或signalAll()方法时,线程会从条件队列中出队,移动到AQS的阻塞队列中,等待获取锁