共享模式与基于 Condition 的等待 / 通知机制实现(5)
- UID
- 1066743
|
共享模式与基于 Condition 的等待 / 通知机制实现(5)
Condition的signal()实现原理上面的代码分析了构建Condition等待队列–释放锁–等待的过程,接着看一下signal()方法通知是如何实现的:
1
2
3
4
5
6
7
| public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
|
首先从第2行的代码我们看到,要能signal(),当前线程必须持有独占锁,否则抛出异常IllegalMonitorStateException。
那么真正操作的时候,获取第一个waiter,如果有waiter,调用doSignal方法:
1
2
3
4
5
6
7
8
| private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
|
第3行~第5行的代码很好理解:
- 重新设置firstWaiter,指向第一个waiter的nextWaiter
- 如果第一个waiter的nextWaiter为null,说明当前队列中只有一个waiter,lastWaiter置空
- 因为firstWaiter是要被signal的,因此它没什么用了,nextWaiter置空
接着执行第6行和第7行的代码,这里重点就是第6行的transferForSignal方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!compareAndSetWaitStatus(node, 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 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
|
方法本意是将一个节点从Condition队列转换为AbstractQueuedSynchronizer队列,总结一下方法的实现:
- 尝试将Node的waitStatus从CONDITION置为0,这一步失败直接返回false
- 当前节点进入调用enq方法进入AbstractQueuedSynchronizer队列
- 当前节点通过CAS机制将waitStatus置为SIGNAL
最后上面的步骤全部成功,返回true,返回true唤醒等待节点成功。从唤醒的代码我们可以得出一个重要结论:某个await()的节点被唤醒之后并不意味着它后面的代码会立即执行,它会被加入到AbstractQueuedSynchronizer队列的尾部,只有前面等待的节点获取锁全部完毕才能轮到它。
代码分析到这里,我想类似的signalAll方法也没有必要再分析了,显然signalAll方法的作用就是将所有Condition队列中等待的节点逐一队列中从移除,由CONDITION状态变为SIGNAL状态并加入AbstractQueuedSynchronizer队列的尾部。 |
|
|
|
|
|