condition的几个api和object的几个方法非常相似,但是对于锁的控制力度更加强大了,比如控制粒度更细了,提供了更多的功能:
在一定时间内等待..等等。
那么就先来看一下平时的使用吧。
ReentrantLock lock = new ReentrantLock();
Condition condition= lock.newCondition();//构造方法,基本属于什么都没做,那么我就不讲这个了
condition.await();
condition.signal();
1.await()方法。
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//初始化链表的功能,设置当前线程为链尾
Node node = addConditionWaiter();
//释放锁
int savedState = fullyRelease(node);
int interruptMode = 0;
//将该线程阻塞:和后续的signal相呼应
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//线程被唤醒了,但是需要重新放入aqs中
//重新自旋+阻塞去尝试获取锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
private Node addConditionWaiter() {
//初始化等待链表
Node t = lastWaiter;
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;
}
2.signal()方法。
//总体来说,signal方法没有那么复杂
public final void signal() {
//判断调用signal的是不是占有锁的线程
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//从这里可以看出,wait和signal是排队了的,是从第一个开始唤醒
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
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;
//放入aqs的队尾中:并返回之前队列的前一个节点
Node p = enq(node);
int ws = p.waitStatus;
//如果前一节点本来就是唤醒状态或者前一节点正在被其他线程唤醒,那么也唤醒自己
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
3.signalAll()方法。见名知其意,大概是将全部唤醒,放入aqs队列中。
//感觉差不多的结构,如果有相似的,就不再详细讲解了。
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
//哈哈,果然是这样,就是一个do while循环,一个个唤醒,因此也应该是存在排队的问题
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}