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;}@Overridepublic 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;}//消费者步骤:@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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;
}打印结果:
----------------------------------------------------------------------
生产者生产了一个汉堡包
生产者生产了一个汉堡包
消费者拿出了汉堡包
消费者拿出了汉堡包
生产者生产了一个汉堡包
消费者拿出了汉堡包
注:【因为阻塞队列有锁,锁会强制线程获取最新共享数据,则不会出现共享数据问题】
【阻塞队列的锁在内部,我们加上的打印语句不在锁内,所以打印出来不均匀】