概述
生产/消费者问题是个非常典型的多线程问题,涉及到的对象包括生产者、消费者、仓库和产品。
他们之间的关系如下:
- 生产者仅仅在仓储未满时候生产,仓满则停止生产。
- 消费者仅仅在仓储有产品时候才能消费,仓空则等待。
- 当消费者发现仓库没产品可消费时候会通知生产者生产。
- 生产者在生产出可消费产品时候,应该通知等待的消费者去消费。
生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间中取走产品,当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞。
存放的仓库
public class Basket {
private volatile Object data;
// 生产者向仓库中存放数据
public synchronized void product(Object data) {
// 如果仓库中有数据,生产者进入阻塞等待 直到被其他线程唤醒
while (this.data != null)
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 如果没有数据 则生产者 进行 生产
this.data = data;
System.out.println(Thread.currentThread().getName() + "生产了一个日期" + data);
this.notifyAll(); // 唤醒在当前对象上的处于wait的所有线程
}
// 消费者从仓库中 消费数据
public synchronized void consume() {
// 如果仓库中没有数据,消费者进入阻塞状态等待 直到被其他线程唤醒
while (this.data == null)
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 如果有数据 进行消费操作
System.out.println(Thread.currentThread().getName() + "消费了一个数据" + data);
this.data = null;
this.notifyAll(); // 唤醒在当前对象上的处于wait的所有线程
}
}
生产者
public class Producer extends Thread {
private Basket basket;
// 使用构造器 传入 对应的basket
public Producer(Basket basket) {
this.basket = basket;
}
@Override
public void run() {
// 生产者生产20次日期对象
for (int i = 0; i < 20; i++) {
Object data = new Date();
basket.product(data);
}
}
}
消费者
public class Consum extends Thread {
private Basket basket;
public Consum(Basket basket) {
this.basket = basket;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
basket.consume();
}
}
}
测试类
public class Test {
public static void main(String[] args) {
Basket basket = new Basket();
Thread t1 = new Consum(basket);
t1.start();
Thread t2 = new Producer(basket);
t2.start();
}
}
为什么要使用生产者/消费者模式?
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,所以便有了生产者和消费者模式。
生产者/消费者模型优点
1、解耦。因为多了一个缓冲区,所以生产者和消费者并不直接相互调用,这一点很容易想到,这样生产者和消费者的代码发生变化,都不会对对方产生影响,这样其实就把生产者和消费者之间的强耦合解开,变为了生产者和缓冲区/消费者和缓冲区之间的弱耦合
2、通过平衡生产者和消费者的处理能力来提高整体处理数据的速度,这是生产者/消费者模型最重要的一个优点。如果消费者直接从生产者这里拿数据,如果生产者生产的速度很慢,但消费者消费的速度很快,那消费者就得占用CPU的时间片白白等在那边。有了生产者/消费者模型,生产者和消费者就是两个独立的并发体,生产者把生产出来的数据往缓冲区一丢就好了,不必管消费者;消费者也是,从缓冲区去拿数据就好了,也不必管生产者,缓冲区满了就不生产,缓冲区空了就不消费,使生产者/消费者的处理能力达到一个动态的平衡
生产者/消费者模式的作用
- 支持并发
- 解耦
- 支持忙闲不均
生产者 消费者 写法2.0
这个版本的生产者消费者相对于上面 更加的简单,而且看起来更加加滴牛🍺。
public class product {
private final static Lock lock = new ReentrantLock();
private final static Condition c1 = lock.newCondition();
private final static Condition c2 = lock.newCondition();
private Object temp = null; //临界资源
private int count = 0; //只是一个简单的计数器,可以不要
//生产 方法
public void pro() throws Exception {
try {
lock.lock();
for (int i = 0; i < 20; i++) {
while (temp != null)
try {
c1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
temp = "一个东东";
Thread.sleep(1000);
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "生产了。。" + temp + "-----" + count);
c2.signal();
}
} finally {
lock.unlock();
}
}
//消费者方法
public void con() throws Exception {
try {
lock.lock();
for (int i = 0; i < 20; i++) {
while (temp == null)
try {
c2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread.sleep(1000);
count++;
System.out.println(Thread.currentThread().getName() + "消费了..." + temp + "-----" + count);
temp = null;
c1.signal();
}
} finally {
lock.unlock();
}
}
//main方法 new了本类product的对象,同时创建了两个线程,分别 执行 生产以及消费的方法
public static void main(String[] args) {
product pp = new product();
new Thread(() -> {
try {
pp.pro();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
pp.con();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
抗吧文工团 编外人员