0
点赞
收藏
分享

微信扫一扫

多线程经典案例(生产者--消费者)

天涯学馆 2022-03-20 阅读 48

示例讲解:

本示例模拟中生产者由“厨师”担任,消费者由“顾客担任”,模拟做菜与上菜的一个简单操作。类之间的关系如图:

初始代码块分解:

       Food类为食物类,内置了set(Food food)生产食物和get(Food food)消费取走食物方法,Food类有两个属性分别是Name(菜品名称)、Desc(菜品描述)并对其进行属性封装;休眠操作模拟了菜品制作以及取出所花费的时间。

/**
 * 食物类
 */
class Food {
    private String Name;//菜品名称
    private String Desc;//菜品描述

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public String getDesc() {
        return Desc;
    }

    public void setDesc(String desc) {
        Desc = desc;
    }


    @Override
    public String toString() {
        return "Food{" +
                "Name='" + Name + '\'' +
                ", Desc='" + Desc + '\'' +
                '}';
    }

    public Food() {
    }

    public Food(String name, String desc) {
        Name = name;
        Desc = desc;
    }

    /*
    * 生产食物set
    * */
    public void set(String Name, String Desc) {
        this.setName(Name);
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.setDesc(Desc);
    }

    /*
  * 消费食物get
  * */
    public void get() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(this.getName() + "-->" + this.getDesc());
    }
}

        Producter类为生产者类,实现了现成的Runnable接口 ,在此类中会生成两种菜品“糖醋里脊”、“辣子鸡丁”,奇数为前者,偶数为后者。

*
* 生产对象类
* */
class Producter  implements Runnable {
    private Food food;

    public Producter(Food food) {
        this.food = food;
    }

    public Producter() {

    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i % 2 == 0) {
                food.set("糖醋里脊", "酸甜口味");
            } else {
                food.set("辣子鸡丁", "麻辣口味");
            }
        }
    }
}

        Customers类为消费者类,同样实现了线程的Runnable接口,在此类会逐步取出所有菜品;

/*
* 消费者对象Customers
* */
class Customers implements Runnable {
    private Food food;

    public Customers() {
    }

    public Customers(Food food) {
        this.food = food;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            food.get();
        }
    }
}

        测试类ProducterCustomerDemo,开启生产者、消费者这两个线程;

/**
 * Description: thread
 * Created by WuHuaSen .
 * Created Date:  2022/3/20 10:02
 * Version:  V1.0
 */
public class ProducterCustomerDemo {
    public static void main(String[] args) {
        Food food = new Food();
        Producter producter = new Producter(food);
        Customers customers = new Customers(food);
        Thread t1 = new Thread(producter);
        Thread t2 = new Thread(customers);
        t1.start();
        t2.start();
    }
}

        测试结果:

         通过测试结果会发现,菜品和对菜品的描述出现了混淆输出,并且对于生产者“糖醋里脊”、“辣子鸡丁”是交互输出,而测试结果出现了“辣子鸡丁”、“糖醋里脊”连续输出的情况,鉴于以上的问题,我们需要使用线程同步、和线程等待方法去解决这类问题的发生,具体操作如下:

         在Food类中添加一个中间变量flag,此变量为Boolean类型,flag==true时表示可以生产;flag==false时候表示可以消费,并在set()和get()方法中记性判断,并在对应的条件下使用Object下的wait()方法使线程进入等待,以此可以解决菜品名和描述混淆的问题,对于进入等待后的线程需要等待其他线程执行完毕进行notify()手动唤醒后,方会启动,从而解决线程同步的问题,由此问题可解~~

修改后的代码如下:

        Food类:设置中间变量flag,同时给set()和get()设置synchronized保证线程同步,并对两个方法进行判断是否需要进入线程等待,并在相应的执行操作后,进行线程唤醒(使用notify()方法);注:notify()是唤醒线程中的另一随机线程;notifyAll()是唤醒所有的线程。而本示例中只有两个线程所以使用notify()即可~

class Food {
    private String Name;//菜品名称
    private String Desc;//菜// 品描述
    boolean flag = true;//true的时候表示可以生产,false时候可以消费

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public String getDesc() {
        return Desc;
    }

    public void setDesc(String desc) {
        Desc = desc;
    }


    @Override
    public String toString() {
        return "Food{" +
                "Name='" + Name + '\'' +
                ", Desc='" + Desc + '\'' +
                '}';
    }

    public Food() {
    }

    public Food(String name, String desc) {
        Name = name;
        Desc = desc;
    }

    /*
    * 生产食物set
    * */
    public synchronized void set(String Name, String Desc) {//同步
        if (!flag){
            try {
                this.wait();//线程进入等待,让出cpu的时间片。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.setName(Name);
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.setDesc(Desc);
        flag=false;//可以消费
        notify();//唤醒其他任一个线程、
        
    }

    /*
  * 消费食物get
  * */
    public synchronized void get() {//同步
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(this.getName() + "-->" + this.getDesc());
        flag =true;
        notify();//唤醒另一个线程
    }
}

修改代码执行后执行结果~~

面试题:sleep()和wait() 的区别:

        sleep():让当前线程进入休眠状态,同时让出cpu时间片,但不释放对象监视器的所有权;

        wait():让当前线程进入休眠状态,同时让出cpu时间片,释放对象监视器的所有权,等待其他线程通过notify()方法或notifyAll()方法进行唤醒。

举报

相关推荐

0 条评论