简介
condition可以通俗的理解为条件队列。当一个线程在调用了await方法以后,直到线程等待的某个条件为真的时候才会被唤醒。这种方式为线程提供了更加简单的等待/通知模式。Condition必须要配合锁一起使用,因为对共享状态变量的访问发生在多线程环境下。一个Condition的实例必须与一个Lock绑定,因此Condition一般都是作为Lock的内部实现。
condition下面看看这个接口提供的方法:
- await() :造成当前线程在接到信号或被中断之前一直处于等待状态。
- await(long time, TimeUnit unit) :造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
- awaitNanos(long nanosTimeout) :造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。返回值表示剩余时间,如果在nanosTimesout之前唤醒,那么返回值 = nanosTimeout - 消耗时间,如果返回值 <= 0 ,则可以认定它已经超时了。
- awaitUninterruptibly() :造成当前线程在接到信号之前一直处于等待状态。【注意:该方法对中断不敏感】。
- awaitUntil(Date deadline) :造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。如果没有到指定时间就被通知,则返回true,否则表示到了指定时间,返回返回false。
- signal() :唤醒一个等待线程。该线程从等待方法返回前必须获得与Condition相关的锁。
- signal()All :唤醒所有等待线程。能够从等待方法返回的线程必须获得与Condition相关的锁。
这个接口是实现类是在AbstractQueuedLongSynchronizer和AbstractQueuedSynchronizer中的内部类ConditionObject。
下面我们来说说AbstractQueuedSynchronizer 这个版本的
创建这个对象:
ReentrantLock Lock =new ReentrantLock();
Condition condition = Lock.newCondition();
//ReentrantLock类中的
public Condition newCondition() {
return sync.newCondition();
}
//创建对象,方法在AbstractQueuedSynchronizer中
final ConditionObject newCondition() {
return new ConditionObject();
}
这个创建对象其实是调用了AbstractQueuedSynchronizer中的方法,生成了ConditionObject内部类。
方法分析
(1)await():实现考虑中断的等待,若thread中断,则抛出异常。
public final void await() throws InterruptedException {
if (Thread.interrupted())//如果线程中断了抛出异常
throw new InterruptedException();
//对等待队列中节点进行判断若状态不为CONDITION,则删除
//创建节点并添加到等待对队列尾部,
Node node = addConditionWaiter();
//释放当前锁释放成功并唤起该节点的后继节点
long savedState = fullyRelease(node);
int interruptMode = 0;
//循环判断当前线程的Node是否在Sync队列中,如果不在,则park
while (!isOnSyncQueue(node)) {
//等待
LockSupport.park(this);
//当前线程在等待过程中是否发生中断,设置interruptMode的值来标志中断状态
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//开始执行后,获取锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
//将清除CONDITION队列中所有非CONDITION状态的节点
unlinkCancelledWaiters();
//检测线程状态,中断
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
private Node addConditionWaiter() {
Node t = lastWaiter;
//检查CONDITION队列中节点是否为cancelled状态
if (t != null && t.waitStatus != Node.CONDITION) {
//不是CONDITION状态删除CONDITION队列中不是CONDITION状态的节点
unlinkCancelledWaiters();
t = lastWaiter;
}
//新建一个CONDITION节点添加到CONDITION队列队尾
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
final int fullyRelease(Node node) {
boolean failed = true;
try {
//获取当前锁重入的次数
int savedState = getState();
//释放锁
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
//释放锁失败,则当前节点的状态变为cancelled
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
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);
}
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
//检测当前线程的状态
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
final boolean transferAfterCancelledWait(Node node) {
//将该节点状态由CONDITION变成0,调用enq将该节点从CONDITION队列添加到CLH队列中
//(但是在CONDITION队列中的nextWaiter连接并没有取消)
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node);
return true;
}
//循环检测该node是否已经成功添加到加锁队列中
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
总结:
await只能在当前线程获取了锁之后调用。因此CLH队列和CONDITION队列的情况为:当前处于CLH队列队首的节点调用await方法,新new一个node,添加到CONDITION队列队尾,然后在CLH队列队首释放当前线程占有的锁,唤醒后继节点。当前线程以新node的形式在CONDITION队列中park,等待被唤醒。
具体过程:
1.清理CONDITION队列中状态不为CONDITION的节点,将该线程封装成node,状态为CONDITION ,并添加到队尾
2.尝试释放当前占有的锁,释放成功唤醒该节点在加锁对列中的后继节点
3.循环调用isOnSyncQueue方法检测node是否再次到加锁队列中(其他线程调用signal或signalAll时,该线程可能从CONDITION队列中transfer到CLH队列中),如果没有,则park当前线程,等待唤醒,同时调用checkInterruptWhileWaiting检测当前线程在等待过程中是否发生中断,设置interruptMode的值来标志中断状态。如果检测到当前线程已经处于CLH队列中了,则跳出while循环。
4.调用acquireQueued阻塞方法来在加锁队列中获取锁。
5.若存在下个节点删除状态不为Condition的节点
6.检查interruptMode的状态,在最后调用reportInterruptAfterWait统一抛出异常或发生中断。
(2)signal():实现考虑中断的等待,若thread中断,则抛出异常。
public final void signal() {
//判断锁是否是被当前线程独占不是则异常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
//CONDITION队列不为null,则doSignal方法将唤醒CONDITION队列中所有的节点线程
if (first != null)
doSignal(first);
}
//对CONDITION队列中从首部开始的第一个CONDITION状态的节点,执行transferForSignal操作,将node从CONDITION队列中转换到CLH队列中,同时修改CLH队列中原先尾节点的状态
private void doSignal(Node first) {
do {
//当前循环将first节点从CONDITION队列transfer到CLH队列
//从CONDITION队列中删除first节点,
//调用transferForSignal将该节点添加到加锁队列中,成功则跳出循环
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
//两步操作,首先enq将该node添加到CLH队列中,其次若CLH队列原先尾节点为CANCELLED或者对原先尾节点CAS设置成SIGNAL失败,则唤醒node节点;否则该节点在CLH队列总前驱节点已经是signal状态了,唤醒工作交给前驱节点(节省了一次park和unpark操作)
final boolean transferForSignal(Node node) {
//如果CAS失败,则当前节点的状态为CANCELLED
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
//enq将node添加到CLH队列队尾,返回node的prev节点p
Node p = enq(node);
int ws = p.waitStatus;
//如果p是一个取消了的节点,或者对p进行CAS设置失败,则唤醒node节点,让node所在线程进入到acquireQueue方法中,重新进行相关操作
//否则,由于该节点的前驱节点已经是signal状态了,不用在此处唤醒await中的线程,唤醒工作留给CLH队列中前驱节点
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
总结:
对CONDITION队列中第一个CONDITION状态的节点(将该节点以及前面的CANCELLED状态的节点从CONDITION队列中出队),将该节点从CONDITION队列中添加到CLH队列末尾,同时需要设置该节点在CLH队列中前驱节点的状态(若前驱节点为cancelled状态或者给前驱节点执行CAS操作失败,则需要调用park操作在此处唤醒该线程,否则就是在CLH队列中设置前驱节点的signal状态成功,则不用在此处唤醒该线程,唤醒工作交给前驱节点,可以少进行一次park和unpark操作)