在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); }