在AQS队列中通过nextWaiter指针串起来的就是条件队列,实际上是通过ConditionObject来实现的。ConditionObject类实现了Condition接口。Condition 实现可以提供不同于 Object 监视器方法的行为和语义。比如一个对象里面可以有多个Condition,可以注册在不同的condition,可以有选择性的调度线程,很灵活。而Synchronized只有一个condition(就是对象本身),所有的线程都注册在这个conditon身上,线程调度不灵活。
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。Condition 实例实质上被绑定到一个锁上。要为特定 Lock实例获得 Condition 实例,请使用其 newCondition() 方法。
条件变量在某个状态条件到达之前一直挂起该线程,由于多个线程都会访问这个共享状态信息,因此这个状态信息必须是线程安全的,这时就需要有锁来支撑。比如等待一个条件变量需要以原子方式释放获取的锁,并挂起当前线程,这和object.wait()类似。
以下是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;
void signal();
void signalAll();
}
多个wait方法对应object.wait()方法,signal方法对应object.notify()方法,signalAll方法对应object.notifyAll()方法。下面分析每个方法的实现过程:
await操作,前面说过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);
}
1、通过addConditionWaiter把当前线程加到条件队列
/**
* Adds a new waiter to wait queue.
* @return its new wait node
*/
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)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
a、如果条件队列队尾结点waitStatus不为CONDITION,证明该条件队列可能包含被取消的线程,unlinkCancelledWaiters操作正是踢掉这些waitStatus不为CONDITION的结点。
b、新建一个含有当前线程的结点放到条件队列尾部
c、返回新结点
2、然后是释放当前结点拥有的锁,不然会造成死锁,这是通过fullyRelease来实现的
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) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
fullyRelease首先获取state,然后调用release方法释放锁,如果释放成功,直接返回,如果释放失败,证明当前结点存在异常,设置当前结点为CANCEL。
release方法是调用tryRelease来实现的,tryRelease成功后需要唤醒在等待的线程,此处唤醒的是同步队列中的第一个非CANCEL结点。
3、自旋挂起当前线程,直到被唤醒,超时或者被CANCEL.这是通过while循环实现的,如果没有在同步队列中就会一直挂起。是否在同步队列是通过isOnSyncQueue判断的
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
return findNodeFromTail(node);
}
显然waitStatus为CONDITION这种结点,只属于条件队列,不在同步队列中。
4、 获取锁,并从条件队列中移除,表示它已经拿到锁了。获取锁是通过acquireQueued实现的
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
acquireQueued的过程如下:
a、如果当前结点的前一个结点是傀儡节点,就尝试获取锁,如果获取成功就设置头结点为当前结点。
b、到此表明还有其他结点在排除,当前就应该被挂起,挂起当前线程,设置中断标志,然后返回。
整个await就经过这几步(释放锁---加入条件队列----挂起---条件满足进入同步队列),其他await方法过程差不多,这里不再分析了。
signal就是要将条件队列中的第一个结点唤醒,以下是它的具体实现:
/**
* Moves the longest-waiting thread, if one exists, from the
* wait queue for this condition to the wait queue for the
* owning lock.
*
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
* returns {@code false}
*/
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
1、首先判断正在运行是不是当前线程,如果不是,则抛出异常!这是通过 isHeldExclusively来实现的,isHeldExclusively方法在AQS中是一个抽象方法,有多种实现,以下是在ReentrantLock中的实现:
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
2、然后调用doSignal方法唤醒第一个线程
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 (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
doSignal()方法是一个while循环,直到transferForSignal成功为止,transferForSignal做了两件事,1、将结点从条件队列移到同步队列中,2、设置同步中它的前趋结点的waitStatus为SIGNAL,并挂起当前线程。其实doSignal就是不断地完成这样一个transfer(条件队列--->同步队列)操作,直到有一个成功为止。
signalAll就是唤醒条件列表中的所有线程,实现很简单,循环调用signal方法即可。
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
/**
* Removes and transfers all nodes.
* @param first (non-null) the first node on condition queue
*/
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}