生产者和消费者问题的由来是:原先有包子类和蒸笼类,蒸笼类里有两个方法,生成包子和消费包子。如果想实现循环生产包子,同时循环吃包子,用单线程实现不了。现在引入多线程让两个方法可以并发执行
部分结果截图(我的缓冲池只能放一个包子)
生产者消费者实现版本一:
import java.util.Random;
public class Test {
public static void main(String[] args) {
Box box = new Box();//创造一个蒸笼
//创建生产者子类对象
Producer producer = new Producer(box);
//创建消费者子类对象
Consumer consumer = new Consumer(box);
//创建线程对象并起名,两个生产者,两个消费者
Thread p1 = new Thread(producer, "生产者1");
Thread p2 = new Thread(producer, "生产者2");
Thread c1 = new Thread(consumer, "消费者1");
Thread c2 = new Thread(consumer, "消费者2");
//启动线程
p1.start();
p2.start();
c1.start();
c2.start();
}
}
class Box {//蒸笼类,一次只能放一个包子
private Food food;//包子对象
void setFood(Food newFood) {//生产包子
//只有生产者会调用生产包子的方法,输出是哪个生产者生产的包子
food = newFood;//把生产的包子放进蒸笼
System.out.println(Thread.currentThread().getName() + "生产了" + food);
}
void eatFood() {//吃包子
//只有消费者才执行吃包子的方法,输出是哪个消费者吃的包子
System.out.println(Thread.currentThread().getName() + "吃" + food);
food = null;//吃完包子,把包子置为null
}
boolean isEmpty() {//判断蒸笼是否为空
return food == null;//吃完包子后消费者会把包子置为null
}
}
class Food {//包子类
String name;//包子名字
int price;//包子价格
public Food() {
}
public Food(String name, int price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Food{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
class Producer implements Runnable {//生产者
Box box;
public Producer() {
}
public Producer(Box box) {
this.box = box;
}
Food[] foods = {new Food("杭州小笼包", 5),
new Food("天津狗不理", 30), new Food("奶黄包", 2)};
Random random = new Random();//用来随机选择生产哪种包子
@Override
public void run() {
while (true) {//一直生产包子
synchronized (box) {//用box做锁对象是为了能唤醒阻塞在蒸笼对象上的消费者
if (box.isEmpty()) {//蒸笼空了,可以放包子了
int sort = random.nextInt(foods.length);//生成【0,foods.length)的整数
box.setFood(foods[sort]);//把第sort种包子放入蒸笼
box.notifyAll();//唤醒阻塞在蒸笼对象上的消费者来吃包子,
// 不用notify()为了防止有多个生产者,想唤醒消费者,实际只唤醒其它生产者,最终全部线程都在wait状态
} else {//蒸笼有包子
try {
box.wait();//生产者自己阻塞自己
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
class Consumer implements Runnable {//消费者
Box box;
public Consumer(Box box) {
this.box = box;
}
public Consumer() {
}
@Override
public void run() {
while (true) {//一直吃包子
synchronized (box) {//用box做锁对象是为了能唤醒阻塞在蒸笼对象上的生产者
if (!box.isEmpty()) {//蒸笼非空,可以吃包子
box.eatFood();//吃完包子,蒸笼里的food=null
box.notifyAll();//唤醒阻塞在蒸笼对象上的生产者做包子,
// 不用notify(),为了防止有多个消费者,想唤醒生产者,实际只唤醒是其它消费者,最后这个程序终止
} else {//蒸笼空
try {
box.wait();//消费者自己阻塞自己
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
生产者消费者实现版本二:
import java.util.Random;
public class Test {
public static void main(String[] args) {
Box box = new Box();//创造一个蒸笼
//创建生产者子类对象
Producer producer = new Producer(box);
//创建消费者子类对象
Consumer consumer = new Consumer(box);
//创建线程对象并起名,两个生产者,两个消费者
Thread p1 = new Thread(producer, "生产者1");
Thread p2 = new Thread(producer, "生产者2");
Thread c1 = new Thread(consumer, "消费者1");
Thread c2 = new Thread(consumer, "消费者2");
//启动线程
p1.start();
p2.start();
c1.start();
c2.start();
}
}
class Box {//蒸笼类,一次只能放一个包子
private Food food;//包子对象
synchronized void setFood(Food newFood) {//把生产者输入的包子放入蒸笼
// synchronized修饰普通方法锁对象是当前对象,锁住其它生产者
//只有生产者会调用生产包子的方法
if (food == null) {//蒸笼空了,可以放包子了
food = newFood;//包子放入蒸笼
// 输出是哪个生产者生产的包子
System.out.println(Thread.currentThread().getName() + "做了" + food);
this.notifyAll();//唤醒阻塞在蒸笼对象上的消费者来吃包子,
// 不用notify()是为了防止有多个生产者,像唤醒消费者实际只唤醒其它生产者,最后全部线程都在wait状态
} else {//蒸笼有包子
try {
wait();//相当于this.wait(),生产者自己阻塞自己
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized void eatFood() {//吃包子,锁住其它消费者
//只有消费者才执行吃包子的方法
//用box做锁对象是为了能唤醒阻塞在蒸笼对象上的生产者
if (food != null) {//蒸笼非空,可以吃包子
// 输出是哪个消费者吃的包子
System.out.println(Thread.currentThread().getName() + "吃掉" + food);
food = null;//吃完包子,把包子置为null
this.notifyAll();//唤醒阻塞在蒸笼对象上的生产者做包子,
// 不用notify()是为了防止有多个消费者,像唤醒生产者,实际只唤醒是它消费者,最后全部线程都是wait
} else {//蒸笼空
try {
this.wait();//消费者自己阻塞自己
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Food {//包子类
String name;//包子名字
int price;//包子价格
public Food() {
}
public Food(String name, int price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "包子 {" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
class Producer implements Runnable {//生产者
Box box;
public Producer() {
}
public Producer(Box box) {
this.box = box;
}
Food[] foods = {new Food("杭州小笼包", 5),
new Food("天津狗不理", 30), new Food("奶黄包", 2)};
Random random = new Random();//用来随机选择生产哪种包子
@Override
public void run() {
while (true) {//一直生产包子
int sort = random.nextInt(foods.length);//生成【0,foods.length)的整数
box.setFood(foods[sort]);//把第sort种包子放入蒸笼
}
}
}
class Consumer implements Runnable {//消费者
Box box;
public Consumer(Box box) {
this.box = box;
}
public Consumer() {
}
@Override
public void run() {
while (true) {//一直吃包子
box.eatFood();
}
}
}
虚假唤醒:是唤醒不应该唤醒的线程(例如消费者1想用notifyAll()唤醒生产者1,但是消费者2也唤醒了)