1.案例描述
这里以吃饭为例,假设有一个桌子,用来存汉堡包,然后有厨师和消费者,厨师往桌子上放汉堡包,消费者从桌子上取走汉堡包。当两者在一个时间段同时进行多次自己的操作时,很明显这就是多线程编程的生产者消费者实例了。在这里,我们希望厨师每次生产一个汉堡包,消费者就拿走一个汉堡包,如果汉堡包还没有被取走,那么厨师应该等待,而如果桌子上没有汉堡包,则消费者应该等待。
2.案例分析
厨师类(Cooker):实现Runnable接口【通用】,包含放汉堡包的方法
消费者类(Foodie):实现Runnable接口【通用】,包含拿汉堡包的方法
桌子类(Desk):共享数据
生产者消费者线程套路:
//1. while(true)死循环
//2. synchronized锁,锁对象要唯一
//3.判断,共享数据是否结束.[true]结束
//4.判断,共享数据是否结束,[false]没有结束
测试类(Demo1):测试类按如下步骤实现这个案例
(1) 创建桌子对象作为共享数据区域
(2) 创建厨师线程,把桌子对象作为参数传递至构造方法【需创建对应的桌子对象成员变量】,因为厨师需要完成放汉堡包的操作
(3)创建消费者线程,把桌子对象作为对象传递至构造方法【需创建对应的桌子对象成员变量】,因为消费者需要完成拿汉堡包的操作
(4)启动线程
3.代码实现
<1>等待唤醒实现
wait(); :【等待】
notifyAll() :【叫醒等待的所有线程】
public class Demo1 {
public static void main(String[] args) {
Desk desk = new Desk();
Foodie foodie = new Foodie(desk);
foodie.start();
Cooker cooker = new Cooker(desk);
cooker.start();
}
}
//厨师类
public class Cooker extends Thread {
private Desk desk;
public Cooker(Desk desk) {
this.desk = desk;
}
@Override
public void run() {
//生产者步骤:
while (true) {
//同步代码块
synchronized (desk.getLock()) {
//判断汉堡包的数量是否达到
if (desk.getCount() == 0) {
break;
} else {
//1,判断桌子上是否有汉堡包
if (!desk.isFlag()) {
//3.如果没有才生产
System.out.println("生产者生产汉堡包");
// 把汉堡包放在桌上
desk.setFlag(true);
//叫醒等待的消费者开吃
desk.getLock().notifyAll();
} else {
//2.如果有就等待
try {
//使用什么对象当做锁,那么就必须用这个对象去调用等待和唤醒的方法
desk.getLock().wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
//消费者类
public class Foodie extends Thread {
private Desk desk;
public Foodie(Desk desk) {
this.desk = desk;
}
//消费者步骤:
@Override
public void run() {
while (true) {
//同步代码块
synchronized (desk.getLock()) {
//判断汉堡包的数量
if (desk.getCount() == 0) {
break;
} else { //1,判断桌子上是否有汉堡包
if (desk.isFlag()) {
//3,如果有就开吃
System.out.println("吃货吃汉堡包");
//4,吃完之后,桌子上的汉堡包就没有了
//叫醒等待的生产者继续生产汉堡包的总数量减一
desk.setCount(desk.getCount() - 1);
desk.setFlag(false);
desk.getLock().notifyAll();//叫醒等待的所有线程[把生产者唤醒]
} else {
//2,如果没有就等待
//没有就等待[释放锁再等待]
//使用什么对象当做锁,那么就必须用这个对象去调用等待和唤醒的方法
try {
desk.getLock().wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
//桌子类
public class Desk {
//定义一个标记
//true表示有,false表示没有汉堡包
//public static boolean flag;
private boolean flag;
//定义一个汉堡包数,表示消费几个结束
//public static int count = 10;
private int count;
//定义一个锁对象 用于消费者线程和生产者线程用同一把锁
//public static final Object lock =new Object();
private final Object lock =new Object();
public Desk(boolean flag, int count) {
this.flag = flag;
this.count = count;
}
public Desk() {
//无参构造可用于初始化赋值
this(false,3);
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Object getLock() {
return lock;
}
}
打印结果:
----------------------------------------------------------------------
生产者生产汉堡包
吃货吃汉堡包
生产者生产汉堡包
吃货吃汉堡包
生产者生产汉堡包
吃货吃汉堡包
<2>阻塞队列实现
创建阻塞队列实现对象,泛型为String[汉堡包],capacity[队列限量]–1[生产一个拿一个]
原理:阻塞队列的put和take方法内部有锁
//测试类
public class Demo1 {
public static void main(String[] args) {
//创建阻塞队列实现对象,泛型为String[汉堡包],capacity[队列限量]--1[生产一个拿一个]
ArrayBlockingQueue<String> list = new ArrayBlockingQueue<String>(1);
Cooker cooker = new Cooker(list);
Foodie foodie = new Foodie(list);
new Thread(cooker).start();
new Thread(foodie).start();//由于阻塞队列的put和take方法内部有锁,
// 而我们加上的打印语句不在锁内,所以打印出来不均匀
}
}
//厨师类
public class Cooker extends Desk implements Runnable {
private ArrayBlockingQueue<String> list;
public Cooker(ArrayBlockingQueue<String> list) {
this.list =list;
}
@Override
public void run() {
while (true){
//判断汉堡数
if(Desk.count==0){
break;
}
//队列没有则生产
try {
list.put("汉堡包");//添加元素,添加满了则等待
System.out.println("生产者生产了一个汉堡包");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消费者类
public class Foodie extends Desk implements Runnable {
private ArrayBlockingQueue<String> list;
public Foodie(ArrayBlockingQueue<String> list) {
this.list = list;
}
@Override
public void run() {
while (true) {
//判断汉堡数
if(Desk.count==0){
break;
}
//队列有则取出
try {
String s = list.take();//取出元素,取完了则等待
Desk.count--;
System.out.println("消费者拿出了" + s);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//桌子类
public class Desk {
static int count =3;
}
打印结果:
----------------------------------------------------------------------
生产者生产了一个汉堡包
生产者生产了一个汉堡包
消费者拿出了汉堡包
消费者拿出了汉堡包
生产者生产了一个汉堡包
消费者拿出了汉堡包
注:【因为阻塞队列有锁,锁会强制线程获取最新共享数据,则不会出现共享数据问题】
【阻塞队列的锁在内部,我们加上的打印语句不在锁内,所以打印出来不均匀】