多线程通信——生产者和消费者

假设在一个饭店中有一个厨师(生产者)、一个服务员(消费者),还有食物三类。

为了更明显的看到生产者消费者两个线程合作时产生的问题,先在在设置食物属性中间使其休眠。

//食物
static class Food{
    private String name;
    private String taste;
    
    //为了更明显的看到生产者消费者不加休眠产生的问题,在设置中间使其休眠
    public void setNameAndTaste(String name, String taste){
        this.name = name;
        //
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.taste = taste;
    }

    public void get(){
        System.out.println("服务员端走的菜的名称是:" + name + ",味道:" + taste);
    }
}

 接下来厨师和服务生分别循环生产/取走100份饭

public static void main(String[] args) {
    Food f = new Food();
    new Cook(f).start();//厨师线程启动
    new Waiter(f).start();//服务生线程启动
}
//厨师
static class Cook extends Thread{
    private Food f;
    public Cook(Food f) {
        this.f = f;
    }

    @Override
    public void run() {
        //厨师循环生成100份饭
        for(int i=0;i<100;i++){
            if(i%2==0){
                f.setNameAndSaste("老干妈小米粥","香辣味");
            }else{
                f.setNameAndSaste("煎饼果子","甜辣味");
            }
        }
    }
}
//服务生
static class Waiter extends Thread{
   private Food f;
   public Waiter(Food f) {
       this.f = f;
   }
   @Override
   public void run() {
       for(int i=0;i<100;i++){
           //为了更合理(厨师生产一份饭需要100ms,那服务生就休眠100ms)
           try {
               Thread.sleep(100);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           f.get();
       }
   }
}

结果:

我们发现在厨师设置食物的名称和味道的时候已经发生了错乱。

原因是当我们的食物在设置这两个属性之间休眠的100ms过程中,服务员线程调用了get方法取走了饭。相当于只改了名字还没有改味道的时候就被get走了。

这就是多个线程进行协同合作时不协调的问题。

为了解决这个问题大家可能想的是直接加上synchronized同步锁就可以了,厨师做饭时服务员不能取,服务员取时厨师不能做:

//食物
static class Food{
    private String name;
    private String taste;
    
    //为了更明显的看到生产者消费者不加休眠产生的问题,在设置中间使其休眠
    public synchronized void setNameAndTaste(String name, String taste){
        this.name = name;
        //
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.taste = taste;
    }

    public synchronized void get(){
        System.out.println("服务员端走的菜的名称是:" + name + ",味道:" + taste);
    }
}

但是synchronized是非公平锁,极有可能厨师做饭的锁解开之后马上继续做饭,而服务员一直排队无法取饭,或者厨师没有开始做饭,服务员一直取饭。

为了解决这个问题,就需要对厨师和服务员休眠——厨师做完就休眠,服务员端走;服务员取完就休眠,厨师做饭。

首先在食物这里设置一个属性标记是厨师还是服务员在工作,然后工作完之后更改标记的属性,唤醒其他线程并休眠自身。

//食物
static class Food{
    private String name;
    private String taste;
    private boolean flag = true;//true表示厨师生产
    
    //为了更明显的看到生产者消费者不加休眠产生的问题,在设置中间使其休眠
    public synchronized void setNameAndTaste(String name, String taste){
        if(flag){
            this.name = name;
            //
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.taste = taste;
            flag = false;
            this.notifyAll();
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized void get(){
        if(!flag){
            System.out.println("服务员端走的菜的名称是:" + name + ",味道:" + taste);
            flag = true;
            this.notifyAll();
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    }
}

结果没有发生错乱并且交替执行:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值