Condition是JDK1.5引入的新的标准库java.util.concurrent.locks.Condition接口。
Condition接口可作为wait/notify的替代品来实现等待/通知,它为解决过早唤醒问题提供了支持,并解决了Object.wait(long)不能区分返回是否是由于等待超时导致的问题。
Condition实例可以通过Lock.newCondition()来获取,也就是说任意一个显示锁实例的newCondition方法都可以获得一个Condition实例。
而Object.wait/notify则要求其执行线程持有这些方法所属对象的内部锁,比如:synchronize
Condition使用方法与wait/notify类似。如下:
Consumer .java
public class Consumer implements Runnable {
private Queue<String> msg;
private int maxSize;
private Lock lock;
private Condition condition;
public Consumer(Queue<String> msg, int maxSize, Lock lock, Condition condition) {
this.msg = msg;
this.maxSize = maxSize;
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
while (true) {
lock.lock();
while (msg.isEmpty()) {
//如果消息队列为空了
try {
System.out.println("第一次阻塞");
condition.await(); //阻塞当前线程
System.out.println("第二次唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者消费消息:" + msg.remove());
condition.signal(); //唤醒处于阻塞状态下的生产者
lock.unlock();
}
}
}
Product .java
public class Product implements Runnable{
private Queue<String> msg;
private int maxSize;
private Lock lock;
private Condition condition;
public Product(Queue<String> msg, int maxSize, Lock lock, Condition condition) {
this.msg = msg;
this.maxSize = maxSize;
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
int i=0;
while(true){
i++;
lock.lock();
while(msg.size()==maxSize){
//如果生产满了
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产者生产消息:"+i);
msg.add("生产消息:"+i);
condition.signal();
lock.unlock();
}
}
}
注意:我会把源码中每个方法的作用都注释出来,可以参考注释进行理解。代码之后的解释就会尽量少一点。
接下来我们重点分析两个方法,也是最常用的两个方法:await()和signal()
public final void await() throws InterruptedException {
//线程中断,抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//创建node节点,并将此节点加到Condition下的队列的最后一个
Node node = addConditionWaiter();
//让持有该锁的所有线程释放锁(包括重入),同时去唤醒AQS队列中的一个线程
long savedState = fullyRelease(node);
int interruptMode = 0;
//判断此节点是否在AQS队列中
while (!isOnSyncQueue(node)) {
//第一次进来,肯定会返回false,接着将自己阻塞,等待被唤醒
LockSupport.park(this);
//signal唤醒之后,继续往下执行
//判断在等待过程中线程是否被中断,如果线程没中断,则会继续执行循环
//再次去isOnSyncQueue判断,改节点是否在AQS中
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
//表示线程被中断,跳出循环
break;
}
// 当这个线程醒来,会尝试拿锁, 当 acquireQueued 返回 false 就是拿到锁了.
// 将这个变量设置成 REINTERRUPT.表示需要重新中断,不会抛出异常
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
//清理掉当前节点之后状态为cancelled的节点
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
// 如果线程被中断了,需要抛出异常.或者什么都不做
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
进来先判断当前线程有没有中断标记,如果有,直接抛出中断异常,交由上层处理;否则,就需要执行addConditionWaiter方法创建节点,并加载到Condition的队列中
private Node addConditionWaiter() {
//获取最后一个节点
Node t = lastWaiter;
// 如果最后一个节点的状态是cancelled,直接out
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
//创建当前线程的节点,状态为CONDITION
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
如果最后一个节点状态为cancelled,直接清理掉。然后创建当前线程的节点,并初始化当前节点的状态为CONDITION。
紧接着,就要释放锁
final long fullyRelease(Node node) {
boolean failed = true;
try {
//获得当前锁的状态值state
long savedState = getState();
//去释放锁,包括重入锁,全部清0
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
//如果失败,将当前节点状态置为CANCELLED
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
public final boolean release(long arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
这里释放,就和之前所讲的ReentrantLock是一样的,这里就不在重复,可以直接去看之前的文章 深入理解ReentrantLock
final boolean isOnSyncQueue(Node node) {
//判断当前节点的状态
//第一次进来的时候状态肯定为CONDITION ==》返回false
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
//node.prev可以为非null,但尚未排队,
//因为将CAS放入队列的CAS可能会失败。
//因此,我们必须从尾部开始遍历以确保它确实做到了。
//在此方法的调用中,它将始终处于尾部,除非CAS失败(这不太可能),
//否则它将一直存在,因此我们几乎不会遍历太多。
return findNodeFromTail(node);
}
实际上,第一次总是会返回 fasle,从而进入循环, 阻塞自己LockSupport.park(this),至此,Condition 成功的释放了所在的 Lock 锁,并将自己阻塞。
唤醒之后,就会执行下面的if方法
private int checkInterruptWhileWaiting(Node node) {
//是否有中断标记
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
其中THROW_IE 和REINTERRUPT含义如下:
final boolean transferAfterCancelledWait(Node node) {
//CAS操作将node状态变为0
//然后添加到AQS的队列中
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
//添加到AQS的队列中
enq(node);
return true;
}
//如果我们失败,那么我们将无法继续
//直到完成其enq()。
//因为几乎是不可能会失败的,所以采用自旋的方式
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
退出while循环后,继续往下执行acquireQueued(node, savedState) 方法去抢占锁,同时还要去判断interruptMode值得状态不能等于THROW_IE也就是中断状态。
两个方法都成功后。将interruptMode值改为REINTERRUPT,即没有影响,无操作。
抢占锁的方法acquireQueued(node, savedState) 同样在之前的文章 深入理解ReentrantLock中讲到,这里不做解释。
最后,就是清理当前节点后面的状态为CANCELLED的节点
接着,我们来看一下它是如何被唤醒的
public final void signal() {
// 如果当前线程不是持有该锁的线程.抛出异常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//获得第一个节点
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
从头部开始唤醒
private void doSignal(Node first) {
do {
//第一个节点的下一个节点为null
//将lastWaiter变为nul
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
采用do while循环,先去判断当前节点的下一个节点,再去执行transferForSignal(node) 方法
final boolean transferForSignal(Node node) {
//如果CAS将状态值变为0失败,则返回false,继续循环,类似自旋
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
//将节点node加到AQS队列
//返回node的前一个节点
Node p = enq(node);
int ws = p.waitStatus;
//CAS将前一个节点状态置为SIGNAL状态,唤醒当前线程
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
最后使用LockSupport.unpark(node.thread) 方法,唤醒node节点上的线程