一、问题概述
生产者消费者模式是一个十分经典的多线程之间协作的模式,实际上主要包含了两类两类线程:
一类是生产者线程,用于生产数据;
一类是消费者线程,用于消费数据。
为了解耦生产者与消费者的关系,通常会采用共享的数据区域,就像是一个仓库。生产者生产数据后直接放置在共享数据区中,并不需要关心消费者的行为;消费者只需从共享数据区中获取数据,同样也不需要关心消费生产者的行为。
在解决这个问题时需要保证:当共享数据区数据达到上限值时,生产者停止生产;当共享数据区为空时,消费者不可消费。
解决这个问题会用到wait()方法与notify()方法:
wait()方法说明:导致当前线程等待,直到另一个线程调用对象的notify()方法或notifyAll()方法。
使用场景:
1.在数据共享区满时调用该方法,使生产者释放锁;
2.在数据共享区空时调用该方法,使消费者者释放锁。
notify()方法说明:唤醒正在等待对象监视器的单个线程
使用场景:
1.在数据共享区未满时,生产者生产数据放入数据共享区,然后数据共享区调用该方法,通知上一个因为wait()方法释放锁的线程现在可以去获得锁了;
2.在数据共享区未空时,消费者去除数据共享区中数据,然后数据共享区调用该方法,通知上一个因为wait()方法释放锁的线程现在可以去获得锁了。
二、示例代码
首先定义一个市场类,此类中有一个ArrayList,最大容量定为5,然后用synchronized同步锁方法实现两个生产数据与消费数据两个方法。
class Market{
final int MAX = 5;//市场最大容量
private List<String> list;
public Market(List<String> list) {
this.list = list;
}
//生产数据方法
public synchronized void produce() {
if (list.size() < MAX){
//市场中数据未满
System.out.println(Thread.currentThread().getName()
+"线程运行,仓库原有库存:"+list.size()+"个数据");
list.add("a");
System.out.println(Thread.currentThread().getName()
+"线程生产后仓库有库存:"+list.size()+"个数据");
this.notify();//唤醒消费者
} else {
//市场中数据已满
System.out.println("数据已满,"
+Thread.currentThread().getName()+"线程等待");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(new Random().nextInt(100)+1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void consume() {
//市场中数据未空
if(list.size()>0){
System.out.println(Thread.currentThread().getName()
+"线程运行,仓库原有库存:"+list.size()+"个数据");
list.remove(0);
System.out.println(Thread.currentThread().getName()
+"线程消费后仓库有库存:"+list.size()+"个数据");
this.notify();
}else {
//市场中数据已空
System.out.println("数据已空,"
+Thread.currentThread().getName()+"程序等待");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(new Random().nextInt(100)+1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
然后创建生产者与消费者对象,引入同一个Market对象,分别调用其中的生产与消费方法。
class P implements Runnable{
private Market market;
public P(Market market) {
this.market = market;
}
@Override
public void run() {
while (true) {
market.produce();
}
}
}
class C implements Runnable{
private Market market;
public C(Market market) {
this.market = market;
}
@Override
public void run() {
while (true) {
market.consume();
}
}
}
测试类测试。
public class ProCon {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Market market = new Market(list);//创建市场对象
//创建两个生产者与消费者
P p1 = new P(market);
P p2 = new P(market);
C c1 = new C(market);
C c2 = new C(market);
new Thread(p1,"生产者1").start();
new Thread(p2,"生产者2").start();
new Thread(c1,"消费者1").start();
new Thread(c2,"消费者2").start();
}
}
运行结果:
程序会一直循环不停。