多线程生成者消费者问题改进

常用的解决方案中,卖完饭了,会采用Object类的notiryAll的方式唤醒所有线程,这样做其实是浪费和低效的,因为唤醒所有线程的时候,会把卖饭的线程也唤醒。现在换用Lock锁的方式来解决,一个Lock可以new多个Condition出来,通过condition来唤醒对应condition上等待的线程。

卖饭类:

package com.sniper.thread.lock.domain;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
 * 餐馆,负责卖饭
 * @author audaque
 *
 */
public class Restaurant implements Runnable {
 
 public void sale() {
  while(true) {
   MyLock.MY_LOCK.lock();
   List<Food> foods = Window.foods;
   if(foods.size() > 0) {
    Food food = foods.get(0);
    System.out.println(Thread.currentThread().getName() + "卖了第" + food.getId() + "号食物。。。");
    
    /*try {
     TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }*/
    
    //卖出一份,减少一份
    foods.remove(0);
    
    //MyLock.CON.signal();//随机唤醒等待中的一条线程
    MyLock.COOK_CON.signalAll();;//唤醒等待中全部线程
   } else {
    System.out.println(Thread.currentThread().getName() + ":食物卖完了,我休息了。。。");
    
    try {
     MyLock.R_CON.await();
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
   MyLock.MY_LOCK.unlock();
  }
 }
 @Override
 public void run() {
  sale();
 }
 
}

做饭类:

package com.sniper.thread.lock.domain;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
 * 厨师,负责做饭
 * @author audaque
 *
 */
public class Cook implements Runnable {
 
 public static int num = 1;
 
 public void make() {
  while(true) {
   MyLock.MY_LOCK.lock();
   List<Food> foods = Window.foods;
   if(foods.size() <= Window.max_num) {
    Food food = new Food(num);
    foods.add(food);
    
    System.out.println(Thread.currentThread().getName() + "做了第" + food.getId() + "号食物。。。");
    
    
    /*try {
     TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }*/
    
    num++;
    
    /*
     * notify随机唤醒一条线程,容易产生问题
     * 假设三个做饭的,三个卖饭的
     * 在某一瞬间三个卖饭的都处于等待,两个做饭的也处于等待
     * 最后一个做饭的做好一份饭之后,随机唤醒其中一条,假如唤醒的是做饭线程,
     * 此时,两条醒着的做饭线程判断foods.size() > Window.max_num(1)
     * 然后两条做饭线程也休息了,此时,六条线程都会处于等待状态,没人会唤醒他们
     * 也就是死锁
     */
    //MyLock.CON.signal();//随机唤醒等待中的一条线程
    MyLock.R_CON.signalAll();;//唤醒等待中全部线程
   } else {
    System.out.println(Thread.currentThread().getName() + ":窗口放满食物了,我休息了。。。");
    try {
     MyLock.COOK_CON.await();
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
   MyLock.MY_LOCK.unlock();
  }
 }
 @Override
 public void run() {
  make();
 }
 
}

窗口,从窗口取饭卖,做完饭往窗口放:

package com.sniper.thread.lock.domain;
import java.util.ArrayList;
import java.util.List;
/**
 * 窗口,厨师做完饭往窗口放,餐馆从窗口拿饭卖
 * @author sniper
 *
 */
public class Window {
 
 public static List<Food> foods = new ArrayList<Food>();
 
 //窗口最大容纳食物数量
 public static int max_num = 1;
 
}

食物类:

package com.sniper.thread.lock.domain;
/**
 * 食物
 * @author sniper
 *
 */
public class Food {
 
 private int id;
 
 private String name;
 public Food(int id) {
  super();
  this.id = id;
 }
 public int getId() {
  return id;
 }
 public void setId(int id) {
  this.id = id;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 
}

App:

package com.sniper.thread.lock.domain;
public class App {
 
 public static void main(String[] args) {
  Cook cook = new Cook();
  //三条做饭线程
  for(int i=0; i<3; i++) {
   new Thread(cook).start();
  }
  
  Restaurant r = new Restaurant();
  //十条卖饭线程
  for(int i=0; i<3; i++) {
   new Thread(r).start();
  }
 }
 
}

锁类:

package com.sniper.thread.lock.domain;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyLock {
 
 public static final Lock MY_LOCK = new ReentrantLock();
 
 public static final Condition R_CON = MY_LOCK.newCondition();
 
 public static final Condition COOK_CON = MY_LOCK.newCondition();
 
 private MyLock() {}
 
}

转载于:https://my.oschina.net/sniperLi/blog/489425

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值