文章目录
更多关于Java并发编程的文章请点击这里:Java并发编程实践(0)-目录页
Condition在Java并发编程中是一个十分常用的接口,被称为条件变量,常与显式锁和可重入锁一起使用,它可以在某些条件下使线程进行休眠或者唤醒,本文将以实现生产者-消费者模式的队列作为demo让大家对条件变量 有初步的了解。
一、并发编程中的条件变量
1.1、从生产者-消费者模型理解条件变量
生产者-消费者模型是一种常见的模型,在《Java并发编程实践》中有一个例子很好地解释了这种模式:
两个洗盘子的劳动分工也是一个与生产者-消费者设计相类似的例子:一个人洗盘子,并把洗好的盘子放到盘子架上,另一个人从盘子架上得到盘子,并把它烘干。在这个场景中,盘子架充当了阻塞队列;如果架子上没有盘子,消费者会一直等待,直到有盘子需要烘干,如果盘子架被放满了,那么生产者会停止洗盘子,直到架上有新的空间可用。
在这个例子中哪里看出了条件变量呢?
这个条件变量就是盘子架是否满了,当盘子架满了,那么生产者等待盘子架有空余的空间时才开始工作,当盘子架空了,消费者等待其有碗了才开始工作,这就可以很好地理解条件变量了:条件变量就是在某些条件下使线程进行休眠或者唤醒的一种工作机制。
1.2、Condition接口
Condition接口
是Java并发编程中的环境变量,它位于java.util.concurrent.locks包下,常与显式锁一起使用,使用Lock.newCondition()获得Condition对象。
public interface Condition {
void await() throws InterruptedException;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
void signal();
void signalAll();
}
1.3、Condition接口方法
以上是Condition接口定义的方法,await
对应于Object.wait
,signal
对应于Object.notify
,signalAll
对应于Object.notifyAll
。特别说明的是Condition的接口改变名称就是为了避免与Object中的wait/notify/notifyAll的语义和使用上混淆,因为Condition同样有wait/notify/notifyAll方法。
- await()方法:
造成当前线程在接到信号或被中断之前一直处于等待状态。 - signal()方法:
唤醒特定的等待线程。 - signalAll()方法:
唤醒所有的等待线程。
二、实现一个生产者-消费者中的条件队列
2.1、条件变量的一般使用模式
lock.lock();
try{
while(条件判断){
acondition.await();
}
//dosomething...
bconditon.signal();
}finally{
lock.unlock();
}
2.2、使用条件变量实现一个生产者-消费者模式的队列
- ConditionQuene
static class ConditionQuene{
//quene长度
private Integer size;
//可重入锁
private Lock lock = new ReentrantLock();
//条件变量(对列满或空)
private Condition isFull = lock.newCondition();
private Condition isEmpty = lock.newCondition();
//无界队列
private BlockingQueue<Integer> quene = new LinkedBlockingQueue<Integer>();
public ConditionQuene(Integer size){
this.size = size;
}
//向有界队列中添加元素
public void add(Integer value) throws InterruptedException {
lock.lock();//执行到该处的线程获得锁
try {
while(quene.size() == size){
//队列已满,让线程在 isFull这个条件变量中等待
isFull.await();
}
quene.add(value);
System.out.println("线程"+Thread.currentThread().getName()+"已将元素"+value+"已经放入队列中,当前空余容量为:"+(size - quene.size()));
isEmpty.signal();//唤醒在 isEmpty 条件变量下等待的线程
}finally {
lock.unlock();//释放锁
}
}
//向有界队列中删除元素
public void delete(Integer value) throws InterruptedException {
lock.lock();//执行到该处的线程获得锁
try {
while(quene.size() == 0){
//队列为空,让线程在 isEmpty 这个条件变量中等待
isEmpty.await();
}
quene.remove(value);
System.out.println("线程"+Thread.currentThread().getName()+"已将元素"+value+"已经从队列中删除s,当前空余容量为:"+(size - quene.size()));
isFull.signal();//唤醒在 isFull 条件变量下等待的线程
}finally {
lock.unlock();//释放锁
}
}
}
- main方法测试
public static void main(String[] args) throws InterruptedException {
//线程调度器->含有两个线程
Executor executor = Executors.newFixedThreadPool(2);
//创建一个只能容纳5个元素的条件队列
ConditionQuene conditionQuene = new ConditionQuene(5);
executor.execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
conditionQuene.add(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
executor.execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
conditionQuene.delete(i);
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
执行结果: