概述
Condition是Reerantlock 实现了 wait- notify 模式的类
synchronized锁 wait- notify 模式 实现的 是 Object.wait(),Object.notify()。
so 重入锁 Reerantlock 也要有wait- notify 模式 的实现,他就是 Condition。
执行 await 和signal 的前后必须有Reerantlock.lock()和Reerantlock.unlock,否则或抛出IllegalMonitorStateException异常。
Condition在ArrayBlockingQueue和 LinkedBlockingQueue中有重要应用。实现存数据和取数据方法之间的相互等待和唤醒。
Reerantlock中创建 Condition的方法
public Condition newCondition() {
return sync.newCondition();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
跟踪发现 ConditionObject是 AbstractQueuedSynchronizer 的内部类。
源码分析
await
await 和signal交互逻辑
ConditionObject中维护一个新的 waiter队列,执行await后创建新节点入队,然后释放锁,while循环检查自己是否在锁等待队列中,没有则进入阻塞。signal 按照FIFO的顺序从waiter队列中取出结点 ,入队锁等待队列。然后 signal 所在的线程锁释放后,await 阻塞被释放,执行后续处理。
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();//入队 waiter队列
int savedState = fullyRelease(node);//释放当前lock,执行await 需要释放锁。
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);
}
await执行分析: 初始化 waiter队列,新创建节点 ,并入队,然后释放锁,检查自己是否在锁等待队列中,在队列中就执行park阻塞,不在队列中证明 singal已经将 刚才入队的节点 取出放入了等待队列,无需执行park阻塞,执行acquireQueued()根据节点状态获取锁成功后,如果阻塞过程中执行了打断,抛出异常,或记录打断状态。
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(Thread.currentThread(), Node.CONDITION);
if (t == null)//如果尾部是null,初始化节点
firstWaiter = node;
else//将节点 挂到尾部
t.nextWaiter = node;
lastWaiter = node;
return node;
}
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
在await过程中执行了打断,如果是在signal前执行,说明 await在执行LockSupport.park(this);中被打断,需要抛出异常,如果是在signal后执行,打断没有起作用要重新打断。
//将取消条件等待的结点从条件队列转移到同步队列中
final boolean transferAfterCancelledWait(Node node) {
//如果这步CAS操作成功的话就表明中断发生在signal方法之前
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
//状态修改成功后就将该结点放入同步队列尾部
enq(node);//上面的cas操作执行后,再执行signal 就会跳过这个node。
return true;
}
//到这里表明CAS操作失败, 说明中断发生在signal方法之后
while (!isOnSyncQueue(node)) {
//如果sinal方法还没有将结点转移到同步队列, 就通过自旋等待一下
Thread.yield();
}
return false;
}
signal
public final void signal() {
if (!isHeldExclusively())//检查是否持有互斥锁,没有就抛出异常
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {//从 firstWaiter 开始向后 出队节点
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;//全部出队完成,更新lastWaiter
first.nextWaiter = null;
} while (!transferForSignal(first) &&(first = firstWaiter) != null);
//将waiter队列中的node转移到 锁等待队列中,一直循环直到成功一次或waiter队列空了
}
doSignal 就是将waiter队列中的node转移到 锁等待队列中,一致循环直到成功一次或waiter队列空了。
final boolean transferForSignal(Node node) {
//如果成功,可以继续执行,否则证明 这个node被取消了
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
Node p = enq(node);//入队锁等待队列,await的阻塞 等待锁释放后被唤醒
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
doSignalAll
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;// 移除 共享变量对队列的关联,为 出队 waiter队列中全部节点做准备。
do {
//获取下个节点 为后续循环获取节点最准备
Node next = first.nextWaiter;
//unlink first
first.nextWaiter = null;
transferForSignal(first);//转移first到锁等待队列中
first = next;//first 出队,first指向next节点
} while (first != null);
}
doSignalAll 要点分析:
doSignalAll 是将waiter中的node全部转移到等待队列中,如果多个线程已经执行了await. 执行
doSignalAll 后,await会按照 waiter队列中入队的顺序获得锁,排队执行。
注意链表中节点被出队的逻辑 先unlink node from queue, 命名一个变量指向这个node,使用结束后,这个变量指向null。然后这个节点才会被GC掉。 GC的条件:没有任何一个对象和变量名能访问到这个对象实例。在队列中 node被回收的条件:队列中节点 没有任何个对象和变量名能访问到这个node.
疑问分析
Condition 的await和signal是怎样抛出 java.lang.IllegalMonitorStateException的?
Reerantlock 的lock的方法 在获取锁后会save互斥锁持有线程,就是执行lock的当前线程。
signal()执行前会检查这个变量,抛出IllegalMonitorStateException。
await() 执行过程中需要释放锁,释放锁的方法中会检查这个变量,抛出IllegalMonitorStateException
Reerantlock-lock
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
先看一下signal中的 IllegalMonitorStateException检查
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//...
}
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
再看一下await中的 IllegalMonitorStateException检查
public final void await() throws InterruptedException {
//...
int savedState = fullyRelease(node);
//...
}
final int fullyRelease(Node node) {
//...
if (release(savedState))
//...
}
public final boolean release(int arg) {
if (tryRelease(arg)) {//调用的是Reerantlock 的方法
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
Reerantlock 释放方法检测IllegalMonitorStateException
protected final boolean tryRelease(int releases) {
//...
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
//...
}
说明:ConditionObject是AbstractQueuedSynchronize的 内部类,Reerantlock 继承了AbstractQueuedSynchronize ,执行new ConditionObject() 实际上是创建了Reerantlock 内部类 ConditionObject的实例,so 这里 访问tryRelease是 Reerantlock 的实现
总结:Condition提供了 wait-notify机制的API,其实现类ConditionObject基于基于Reetrantlock实现。ConditionObject 本身自维护一个新的等待队列,执行await时创建节点链接到这个队列尾部,进入阻塞,执行signal时从队列头部 取出一个节点放入 Reetrantlock 的锁等待队列。执行Reetrantlock.unlock释放 await中被阻塞的线程,await被释放后 通过 Reetrantlock 的acquireQueue 排队获取锁。
整个Condition功能的实现 简单的概括: await在新队列中加入节点,signal从新队列中取出节点放入AQS的等待队列,借助AQS的加锁,释放,acquireQueue 排队获取锁 实现了 wait-notify机制。