生产消费者模型
在生产者和消费者关系中,生产者是一堆线程,消费者是另一堆线程。生产者在产生数据后可以直接调用消费者处理数据,也可以把数据放在一个缓冲区中,让消费者从缓冲区中取出数据处理。
从调用方式上, 第一种是同步的,生产者在生产出数据后要等待消费者消耗后才能生产下一个数据。第二种是异步的,生产者产生数据后扔入缓冲区,不管数据是否被立即处理。
从线程模型上,第一种是单线程的,第二种是多线程的,就要考虑线程间的协作。
特点:
1、生产者仅仅在仓储未满时候生产,仓满则停止生产。
2、消费者仅仅在仓储有产品时候才能消费,仓空则等待。
3、当消费者发现仓储没产品可消费时候会通知生产者生产。
4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费。
实现方式一:
wait和notify
通过synchronized来保证线程安全,在消息满或者不足的时候wait进行阻塞,然后notifyAll来通知其他监听。
static class Storage {
private Queue<Integer> queue;
private Integer max;
public Storage(Queue<Integer> queue, Integer max) {
this.queue = queue;
this.max = max;
}
private synchronized void produce(Integer msg) {
while (queue.size() > max) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.offer(msg);
this.notifyAll();
}
private synchronized Integer consume() {
while (queue.size() == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Integer result = queue.poll();
this.notifyAll();
return result;
}
}
实现方式二:
Lock和Condition
通过Lock来保证线程安全,通过Condition来实现阻塞和通信,在消息队列满的时候,通过notFull的wait和notEmpty的signalAll来阻塞当前生产者并且通知消费者来消费消息,消息队列空的时候同理。
static class Storage {
private Queue<Integer> queue;
private Integer max;
private Lock lock;
private Condition notEmpty;
private Condition notFull;
public Storage(Queue<Integer> queue, Integer max) {
this.queue = queue;
this.max = max;
lock = new ReentrantLock();
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
private void produce(Integer msg) {
lock.lock();
try {
while (queue.size() > max) {
notFull.await();
}
queue.offer(msg);
notEmpty.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private synchronized Integer consume() {
lock.lock();
Integer result = null;
try {
while (queue.size() == 0) {
notEmpty.await();
}
result = queue.poll();
notFull.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return result;
}
}
实现方式三:
JAVA的实现类ArrayBlockingQueue
ArrayBlockingQueue是一个阻塞队列,在队列满的时候put会阻塞,空的时候take也会阻塞,其内部实现也是基于Lock和Condition来实现的。
static class Storage {
private ArrayBlockingQueue<Integer> queue;
public Storage(Integer max) {
this.queue = new ArrayBlockingQueue<>(max);
}
private void produce(Integer msg) {
try {
queue.put(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private Integer consume() {
try {
return queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}