Condition
多个线程之间通信,JDK1.5以前使用wait和notify,到了JDK1.5我们有了更加高效的Condition。
通过源码分析Condition的实现
ReentrantLock的Condition基本使用
假如要做一个生产者消费者,当队列中没有元素时,生产者要生产,此时消费者线程不应该再去争夺CPU资源,同样如果队列已满,生产者也不该去争夺CPU资源,而是交由消费者取消费,如果通过Synchronized,则无法实现。
此时Codition可以解决上述的问题。我们使用Codndition来做个DEMO。
package cc.ai;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by yangkai.
*
* @author yangkai
* @date 2016/12/9
*/
public class ConditionTest {
public static void main(String[] args) throws InterruptedException {
ReentrantLock reentrantLock = new ReentrantLock();
//定义一个只能存储1个元素的blockingQueue,此处只是示例,实际上
//LinkedBlockingQueue 已经实现了,等待和通知的机制。
LinkedBlockingQueue queue = new LinkedBlockingQueue(1);
Condition consumerCon = reentrantLock.newCondition();
Condition producerCon = reentrantLock.newCondition();
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
//生产者
reentrantLock.lock();
try {
while(queue.size() == 1) {
System.out.println("生产者不能再生产,等着");
producerCon.await();
}
System.out.println("开始生产" + queue.add(1));
consumerCon.signal();//唤醒消费者
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
}).start();
}
// TimeUnit.SECONDS.sleep(1);
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
//消费者
reentrantLock.lock();
try {
while(queue.size() == 0) {
System.out.println("消费者不能再消费,等着");
consumerCon.await();
}
System.out.println("开始消费:"+ queue.remove());
producerCon.signal();//唤醒生产者
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
}).start();
}
}
}
运行结果:
从上面的运行结果可以看到,当队列只能容纳一个元素时,生产者和消费者是一对一单独出现的。
Conditon的await方法
用Synchronized中的wait和notify也能实现上面的生产者消费者啊,为什么我要使用Condition,它有什么优势?
下面我们通过源码来分一下Condition的await方法,但是在分析之前,我们要知道,调用lock.newCondition()的时候,java做了什么事。
ReentrantLock的newCondition实现.
public Condition newCondition() {
return sync.newCondition();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
返回的ConditionObject在AQS中定义:
什么都没有,貌似看不出什么。再往下看await。
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();//将node节点加入到codition队列
int savedState = fullyRelease(node);//尝试获取锁
int interruptMode = 0;
while (!isOnSyncQueue(node)) {//当前节点是否在lock队列中(肯定不在)
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)//此时节点等待被激活。
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
总结下来就是,codition自己也是一个队列,当调用await的时候,就向condition后面添加一个节点。
Conditon的signal方法
既然codition的await是向condition队列添加节点,但是节点并未添加到lock队列中啊,只有被添加到lock队列中的节点才有可能获得执行机会,所以我们大胆猜测,signal的功能就是从codition队列获取节点,然后放入lock队列中。现在看下代码实现:
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {//取出condition的节点,调用transferForSignal去添加到lock节点
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
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);//是不是很熟悉。将condition的节点添加到lock队列中
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}