前置条件
- 生产者生产数据,消费者消费数据,所以需要一个公共的池子来支持,生产者生产了数据放入池子中,消费者从这个池子里消费,我们用队列来维护这个池子。
- 生产者生产完之后需要告知消费者,池子里的数据被消费完,消费者需要等待生产者生产,所以我们需要一个等待通知机制来处理这块逻辑 -- 代码中采用condition 搭配lock来处理。
- 池子有其大小容量,所以需要个变量来维护这个容量。
定义变量
// 容量
private int capacity = 10;
// 队列
private Queue<Integer> queue = new ArrayBlockingQueue<>(capacity);
private ReentrantLock lock = new ReentrantLock();
private Condition add = lock.newCondition();
private Condition remove = lock.newCondition();
简单生产者
class Producer extends Thread{
@Override
public void run() {
produce();
}
private void produce(){
lock.lock();
try {
while (true) {
if (queue.size() == capacity) {
try {
System.out.println("生产者进入等待");
add.await();
System.out.println("生产者不等了");
} catch (InterruptedException e) {
}
}
queue.offer(1);
remove.signal();
Thread.sleep(1000);
System.out.println("生产者生产 1 条,队列长度:" + queue.size());
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
简单消费者
class Consumer extends Thread{
@Override
public void run() {
consume();
}
private void consume(){
lock.lock();
try {
while (true) {
if (queue.isEmpty()) {
try {
System.out.println("消费者进入等待");
remove.await();
System.out.println("消费者不等了");
} catch (InterruptedException e) {
}
}
Integer some = queue.poll();
add.signal();
Thread.sleep(1000);
System.out.println("消费者消费 1 条:" + some + " 队列剩余:" + queue.size() + "个元素");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
输出
一个问题
为什么这里生产者生产完了消费者才消费,消费者消费完了生产者才生产呢?
是因为生产者消费者共用同一个lock,await()方法触发了才会释放锁,代码中await在isEmpty()和size == capacity的条件判断中,所以才会发生这种情况
解决方案
private ReentrantLock lock1 = new ReentrantLock();
private ReentrantLock lock2 = new ReentrantLock();
private Condition add = lock1.newCondition();
private Condition remove = lock2.newCondition();
我们用两个lock,condition分别对应其中一个即可解决
输出
当然了,生产者执行到打印语句的时候,消费者早都消费完了,所以打印队列长度:0
注意点
lock 需要搭配try-finally进行解锁,condition作用域在lock-unlock之间,类似于wait-notify作用在synchronized块内