生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻。所谓生产者和消费者问题,实际上就是两个线程:
生产者:生产数据以供消费者消费,即生产者线程。
消费者:消费生产者生产的数据,即消费者线程。
在这个过程中,生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为 消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为。
假如有两个角色:吃货、厨师。还有一个辅助角色——桌子。厨师负责做包子,吃货负责吃包子。我们有这么一个场景:
如果桌子上有包子,吃货就吃掉包子,如果桌子上没有包子,吃货就会去叫厨师做包子并且等待包子做好;
如果桌子上没有包子,厨师就会做包子,如果桌子上有包子,厨师就会叫吃货吃包子并且等待包子吃完后再做。
桌子类:普通类。负责定义包子数量,包子有无
厨师类:线程类。这里我们通过实现继承Thread类,重写run方法实现。负责做以下事情:
1.判断是否有包子,决定当前线程是否执行
2.如果有包子,就进入等待状态,如果没有包子,继续执行,生产包子
3.生产包子之后,更新桌子上包子状态,唤醒消费者消费包子
吃货类:线程类。这里我们通过实现继承Thread类,重写run方法实现。负责做以下事情:
1.判断是否有包子,决定当前线程是否执行
2.如果没有包子,就进入等待状态,如果有包子,就消费包子
3.消费包子后,更新桌子上包子状态,唤醒生产者生产包子
desk类的具体代码:
public class desk {
// 判断桌子上有没有包子
public static boolean flag=false;
// 定义包子的数量
public static int count=10;
//定义吃货和厨师的唯一锁
public static final Object obj = new Object();
}
foodie吃货类的具体代码“
public class foodie extends Thread{
@Override
public void run() {
while (true){
synchronized (desk.obj){
// 包子数量剩下0个 结束线程
if (desk.count==0){
break;
}else{
// 桌子上没有包子
if (!desk.flag){
System.out.println("厨师做包子");
// 设置桌子有包子标志
desk.flag = false;
// 包子数量减一
desk.count--;
// 唤醒吃货线程
desk.obj.notify();
}else{
try {
// 如果有包子,则进入等待状态
desk.obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
cooker厨师类的具体实现:
public class cooker extends Thread{
@Override
public void run() {
while (true){
synchronized (desk.obj){
// 包子数量剩下0个 结束线程
if (desk.count==0){
break;
}else{
// 桌子上有包子
if (desk.flag){
System.out.println("吃货吃包子");
// 设置桌子没有包子标志
desk.flag = true;
// 唤醒厨师线程
desk.obj.notify();
}else{
try {
// 如果没有包子,则进入等待状态
desk.obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
测试类:
public class DemoTest {
public static void main(String[] args) {
// 创建厨师类
cooker cooker = new cooker();
// 创建吃货类
foodie foodie = new foodie();
// 启用厨师类线程
cooker.start();
// 启用吃货类线程
foodie.start();
}
}
测试结果:
我们就实现了生产者生产数据,消费者消费数据,以及等待唤醒机制。当然这个程序虽然我们实现了我们想要的效果,但是却不是很好的一种方式。原因就出在desk类的属性是直接对外部暴露出来的public权限,这一点也不“面向对象”。所以接下来我们对他进行了优化,即采用面向对象的思想将desk封装起来。
desk类
public class desk {
private boolean flag;
private int count;
private final Object obj = new Object();
public desk(){
this(false,10);
}
public desk(boolean flag, int count) {
this.flag = flag;
this.count = count;
}
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 getObj() {
return obj;
}
}
cooker类:
public class cooker extends Thread{
private desk desk;
public cooker(desk desk){
this.desk = desk;
}
@Override
public void run() {
while (true){
synchronized (desk.getObj()){
// 包子数量剩下0个 结束线程
if (desk.getCount()==0){
break;
}else{
// 桌子上没有包子
if (!desk.isFlag()){
System.out.println("厨师做包子");
// 设置桌子有包子标志
desk.setFlag(true);
// 唤醒吃货线程
desk.getObj().notify();
}else{
try {
// 如果有包子,则进入等待状态
desk.getObj().wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
foodie吃货类:
public class foodie extends Thread{
private desk desk;
public foodie(desk desk){
this.desk = desk;
}
@Override
public void run() {
while (true){
synchronized (desk.getObj()){
// 包子数量剩下0个 结束线程
if (desk.getCount()==0){
break;
}else{
// 桌子上有包子
if (desk.isFlag()){
System.out.println("吃货吃包子");
// 设置桌子有包子标志
desk.setFlag(false);
// 包子数量减一
desk.setCount(desk.getCount()-1);
// 唤醒厨师线程
desk.getObj().notify();
}else{
try {
// 如果没有包子,则进入等待状态
desk.getObj().wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
测试类:
public class DemoTest {
public static void main(String[] args) {
// 创建一个桌子类
desk d = new desk();
// 创建厨师类
cooker cooker = new cooker(d);
// 创建吃货类
foodie foodie = new foodie(d);
// 启用厨师类线程
cooker.start();
// 启用吃货类线程
foodie.start();
}
}
以上这种方式我们就把desk采用面向对象的方式封装。更加符合Java的面向对象思想。以上就是通过线程唤醒和等待机制实现生产者和消费者。