浅谈多线程生产者和消费者问题(线程的等待和唤醒)

一、概述

        生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻。所谓生产者和消费者问题,实际上就是两个线程:

        生产者:生产数据以供消费者消费,即生产者线程。

        消费者:消费生产者生产的数据,即消费者线程。

        在这个过程中,生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为 消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为。

二、举个栗子

        假如有两个角色:吃货、厨师。还有一个辅助角色——桌子。厨师负责做包子,吃货负责吃包子。我们有这么一个场景:

        如果桌子上有包子,吃货就吃掉包子,如果桌子上没有包子,吃货就会去叫厨师做包子并且等待包子做好;

        如果桌子上没有包子,厨师就会做包子,如果桌子上有包子,厨师就会叫吃货吃包子并且等待包子吃完后再做。

三、系统需求分析:

        桌子类:普通类。负责定义包子数量,包子有无

        厨师类:线程类。这里我们通过实现继承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();
    }
}

测试结果:

浅谈多线程生产者和消费者问题(线程的等待和唤醒)_第1张图片

        我们就实现了生产者生产数据,消费者消费数据,以及等待唤醒机制。当然这个程序虽然我们实现了我们想要的效果,但是却不是很好的一种方式。原因就出在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的面向对象思想。以上就是通过线程唤醒和等待机制实现生产者和消费者。

你可能感兴趣的:(多线程,java,idea)