一、概述
首先,我们需要明白生产者和消费者模型是为了解决多线程之间通信而产生的。其次,需要了解实现线程间通信用到了哪些知识点。先来看一张生产者和消费者模型图:
这里有四个对象,一个是生产者,一个是消费者,一个是容器,最后还有一个是需要生产的商品,虽然在图上没有体现出来。刚开始容器里面没有商品,消费者等待,生产者开始生产,当容器里面有商品了,生产者通知消费者去消费,如果生产的速度比较快,容器满了,那么生产者就需要等待,消费者消费了商品容器有空余,则通知生产者去生产。大概就是这样一个过程。
二、核心知识点
线程间通信主要用到wait()、notify()和notifyAll(),这三个都属于Object的方法,调用wait()方法的线程会执行等待,跟sleep()类似,但也有区别:wait方法等待时会释放对象锁,而sleep方法等待时不会,还有一点就是sleep是Thread类中的方法,而wait属于Object的方法。notify和notifyAll方法都是唤醒等待中的对象,它们的区别是notify只能随机唤醒一个等待中的对象,而notifyAll则会唤醒所有等待中的对象。为了确保我们需要的对象被唤醒,开发中建议使用notifyAll来唤醒。如果其他等待的对象也被唤醒了,可以加一个条件判断,这样能确保需要被唤醒的对象做自己的事情,其他被唤醒的对象也不会受影响,可以参考如下的伪代码:
Object lock = new Object();
synchronized (lock){
//注意:这里用while循环判断,而不是if
while (条件不成立){
lock.wait();
}
//do something
}
//另一个线程
synchronized (lock){
lock.notifyAll();
}
三、线程间通信实现
1、生产者
/**
* 生产者
*/
public class Producer extends Thread {
Container container;
public Producer(Container container) {
this.container = container;
}
@Override
public void run() {
for (int i = 1; i < 31; i++) {
Bread bread = new Bread(i);
container.put(bread);
Log.i("testlog", "生产第" + i + "个面包");
}
}
}
2、消费者
/**
* 消费者
*/
public class Consumer extends Thread {
Container container;
public Consumer(Container container) {
this.container = container;
}
@Override
public void run() {
for (int i = 1; i < 31; i++) {
Bread bread = container.get();
int id = bread.id;
Log.e("testlog", "消费第" + id + "个面包");
}
}
}
3、容器
/**
* 容器
*/
public class Container {
//存数据的队列
private Bread[] breads = new Bread[10];
//存数据的序号
int count;
/**
* 存数据
*
* @param bread
*/
public synchronized void put(Bread bread) {
//容器满了,不能生产
if (count == breads.length) {
try {
//线程阻塞,等待消费者通知生产者
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
breads[count] = bread;
count++;
//存在数据,通知消费
notifyAll();
}
/**
* 取数据
*/
public synchronized Bread get() {
//没有数据,不能消费
if (count == 0) {
try {
//线程阻塞,等待生产者通知消费
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Bread bread = breads[count];
//存在空间,可以生产
notifyAll();
return bread;
}
}
4、商品
/**
* 商品
*/
public class Bread {
//面包的序号
int id;
public Bread(int id) {
this.id = id;
}
}
5、测试代码
Container container = new Container();
Producer producer = new Producer(container);
Consumer consumer = new Consumer(container);
producer.start();
consumer.start();
打印部分结果: