一 前言
这一次在await()
方法的后面讲,所以这里涉及到的一些写过的基础知识就不再重复了。
二 signal()
还是国际惯例,从入口处出发:
// 将Condition的FIFO条件队列中的第一个线程唤醒
public final void signal() {
// 若当前线程不持有锁,则抛出异常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 唤醒FIFO队列中的第一个线程节点
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
很明显,这个功能是要唤醒Condition等待队列中的第一个线程节点,也就是等待队列中的头节点。那么我们下面需要解析的方法有以下几个:
isHeldExclusively()
:这里调用的AQS方法的队列,所以持有当前线程的引用。判断当前线程是否持有锁,有则返回true,反之返回falsedoSignal(Node)
:唤醒指定节点的线程
2.1 isHeldExclusively()
这次讲的是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.2 doSignal(Node)
// 唤醒当前结点
private void doSignal(Node first) {
do {
// 将头节点从等待队列中移除
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
doSignal()将头节点移出等待队列,再调用transferForSignal()方法将节点添加到同步队列中:
// 将节点从条件队列转换到同步队列。 如果成功则返回true。
final boolean transferForSignal(Node node) {
// 如果不能够改变等待状态,则说明该节点已经被cancelled,如果cancelled 那么会继续唤醒下一个节点,
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 将该节点插入同步队列队尾,并且返回node节点的前继节点
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
这里被cancelled的节点,会在其他线程被移出等待队列。
三 signalAll()
这个其实跟signal差不多,只不过signal()
中的doSignal(Node)
方法在transferForSignal(Node)
成功将节点添加到同步队列后就不再进行循环操作了,在这里就无视了transferForSignal(Node)
的返回值。
由于比较容易理解,直接贴不同的地方了了。
// 唤醒Condition的FIFO条件队列中的所有线程
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first); // 其余一样
}
3.1 doSignalAll(Node)
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
这样就讲完signal()
跟signalAll()
两个方法啦。