什么是Condition
- Condition是一个接口
- 它提供了类似Object的监视器方法,配合Lock可以实现 等待/通知模式。
- 相比object的监视器方法
- 它可以支持多个等待队列
- 支持等待时中断
- 支持超时等待
- Condition对象的创建依赖于Lock对象
- 在Lock的源码中我们可以看到
Condition newCondition();
获取 Condition的定义
- 在Lock的源码中我们可以看到
- Condition的实现是在AQS之中实现的
这里我们需要说一下,一个lock对象对应多个condition。
这里的含义相当于是对于同一个锁(资源)下的所有线程,它们之间有着不同的条件关系,所以需要我们分别来定义condition
Condition的使用及源码分析
前面介绍时说到,Condition是在Lock中定义的。
那么我们需要使用Condition时,则需要通过lock对象.newContion()
方法来获取。
然后可以使用condition对象.await()方法,进入等待,同时释放锁。
然后其他对象通过调用condition对象.signal()方法唤醒等待的线程,同时释放锁。
Condition的实现
Condition是一个接口,它具体的实现是在AQS之中的,作为AQS的一个内部类存在。
这里的逻辑比较清晰,因为Condition是依赖于Lock存在的,而Lock又必须要使用AQS,所以这样安排子的话。Lock对象中就一定有AQS,也就一定有Condition了。
在AQS源码中,我们可以找到Condition的实现类ConditionObject:
public class ConditionObject implements Condition, java.io.Serializable
Condition接口并没有定义队列,但是其实现类却是定义了等待队列。
- 这是一个单向的队列
- 维护它是用了firstWaiter,lastWaiter节点
- 此处的节点是复用了AQS中同步队列的节点(所以每个节点中都有线程引用)
- 每一个Condition对象都有一个等待队列
- 一个锁对象可以有多个Condition对象,所以一个锁对象有一个同步队列和多个Condition等待队列
下面来将一下Condition等待、等待队列、通知的流程:
等待:
- 首先需要明确,与wait()一样,能够执行等待与通知的线程都是获取了锁(or同步状态)的线程,所以这里是没有同步问题担心的,由锁帮我们解决了线程安全问题
- 当一个线程执行了await()方法,它会释放锁,然后构造新节点加入到等待队列的队尾,进入等待状态。
- 然后它就在队列里呆着,一直呆着,直到有人signal,唤醒等待队列,将等待队列的firstWaiter(节点)加入到同步队列的队尾。直到自己也进入同步队列为止
通知:
- 与await()一样,执行signal方法的线程也是获取了锁
- 它释放锁,然后等待队列的firstWaiter移动到同步队列中去,然后唤醒该节点。
下面来看一下源码:
首先是await():
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.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
这里是创建一个节点,节点的mode是Condition,然后将其插入到等待队列的尾部
final int fullyRelease(Node node) {
try {
int savedState = getState();
if (release(savedState))
return savedState;
throw new IllegalMonitorStateException();
} catch (Throwable t) {
node.waitStatus = Node.CANCELLED;
throw t;
}
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
可以看到这里释放同步状态,实际需要调用AQS的tryRelease方法,来释放同步状态。
之后便调用LockSupport的方法阻塞线程。
然后来看signal方法:
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);//这里
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&//看这
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);//进入同步队列
int ws = p.waitStatus;
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);//唤醒
return true;
}
这里可以看到,signal时,唤醒的对象是firstWaiter。
之后一层层的调用,先将该节点通过enq函数加入同步队列,然后
将firstWaiter的线程用LockSupport的unpark方法进行唤醒。