快速学习生产者和消费者模型
一、学习生产者和消费这模型之前,你要了解三个概念,生产者,消费者和缓冲区。
1、生产者:产生数据
2、消费者:使用数据
3、缓冲区,存储数据
二、生产者生产数据会保存在缓冲区,消费者会在缓冲区里取数据进行消费。
注意:当缓冲区填满时,生产者不可生产数据,当缓冲区为空时,消费者不可取数据。数据不可重复生产也不可重复获取,这里以id来区分(id 以自增的方式生成)
三,代码
Book为产品,有三个字段,分别是id,name,price。
方式一、lock的方式
public class ProducerAndConsumerByManyToMany {
private final ReentrantLock lock = new ReentrantLock();
private final Condition consumerCondition = lock.newCondition();
private final Condition producerCondition = lock.newCondition();
private int id = 1;
private final int MAX_QUE = 10;
LinkedList<Book> list = new LinkedList<>();
class Consumer implements Runnable{
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
lock.lock();
try {
while (list.isEmpty()){
System.out.println("queue is empty...");
consumerCondition.await();
}
mySleep(200);
System.out.println(list.remove());
mySleep(100);
producerCondition.signal();
} catch (Exception e){
System.out.println(e.getMessage());
} finally {
lock.unlock();
}
}
}
}
class Producer implements Runnable{
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
lock.lock();
try {
while (list.size()==MAX_QUE){
System.out.println("queue is full...");
producerCondition.await();
}
mySleep(200);
list.add(new Book(id+++"","A","¥99.00"));
mySleep(100);
consumerCondition.signal();
} catch (Exception e){
System.out.println(e.getMessage());
} finally {
lock.unlock();
}
}
}
}
private void mySleep(long time){
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ProducerAndConsumerByManyToMany mtm = new ProducerAndConsumerByManyToMany();
new Thread(mtm.new Producer()).start();
new Thread(mtm.new Producer()).start();
new Thread(mtm.new Producer()).start();
new Thread(mtm.new Consumer()).start();
new Thread(mtm.new Consumer()).start();
}
注意:await条件为什么要用while而不是if,原因如下
因为 会有多个消费者或者生产者的情况,以消费者为例:当一个消费者获得lock,来到await循环条件,此时缓冲区(链表)为空,只能等待并释放锁,第二个消费者获得锁,碰巧也为空,也只能等待并释放锁。次时如果生产者生产了一条数据,并唤醒了所有的消费者。如果为if的话,被唤醒的并不会继续做判空,一个消费者获得了锁并消费了数据,释放锁,另一个消费者也获得了锁,此时缓冲区为空,却做了取数据的操作,出现了数据不一致
方式二、通过原子类和阻塞队列的方式
public class ProducerAndConsumerByAtomic {
private final AtomicInteger id = new AtomicInteger(1);
private final int MAX_PRODUCTION = 30;
private final BlockingQueue<Book> queue = new ArrayBlockingQueue<>(3);
class Consumer implements Runnable{
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
try {
System.out.println(queue.take());
mySleep(100);
} catch (Exception e){
System.out.println(e.getMessage());
}
}
}
}
class Producer implements Runnable{
@Override
public void run() {
int a;
while (!Thread.currentThread().isInterrupted()&&(a = id.getAndIncrement())<=MAX_PRODUCTION){
try {
queue.put(new Book(a+"","活着","63¥"));
mySleep(200);
} catch (Exception e){
System.out.println(e.getMessage());
}
}
}
}
private void mySleep(long time){
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ProducerAndConsumerByAtomic Pa = new ProducerAndConsumerByAtomic();
new Thread(Pa.new Producer()).start();
new Thread(Pa.new Producer()).start();
new Thread(Pa.new Consumer()).start();
}
}