生产者-消费者问题笔记

生产者-消费者问题

  • 线程互斥要求
  1. 生产者之间是互斥的,也即同时只能有一个生产者进行生产
  2. 消费者之间是互斥的,也即同时只能有一个消费者进行消费
  3. 生产者消费者之间是互斥的,也即生产者消费者不能同时进行生产和消费
  • 线程同步要求
  1. 容器满时,生产者进行等待
  2. 容器空是,消费者进行等待

Object的synchronized() wait() notifyAll()实现

两个线程,一个产品
//生产消费共10轮
public class Demo{
  public static void main(String[] args){
    Goods goods = new Goods();
    new Thread(()->{
      for(int i=0; i<10; i++){
        try{
          goods.produce();
        }catch(InterruptedException e){
          e.printStackTrace();
        }
      }
    },"PRODUCE").start();
    new Thread(()->{
      for(int i=0; i<10; i++){
        try{
          goods.consume();
        }catch(InterruptedException e){
          e.printStackTrace();
        }
      }
    },"CONSUME").start();
  }
}

class Goods{
  private int number = 0;
  public synchronized void produce() throws InterruptedException{
    if(number != 0){ //判断
      this.wait();
    }
    number++;//干活
    System.out.println(Thread.currentThread().getName() + "Produce" + number);
    this.notifyAll(); //通知
  }
  public synchronized void consume() throws InterruptedException{
    if(number != 0){ //判断
      this.wait();
    }
    number--;
    System.out.println(Thread.currentThread().getName() + "Consume" + number);
    this.notifyAll(); //通知
  }
}
四个线程,一个产品
//生产消费共10轮
public class Demo{
  public static void main(String[] args){
    Goods goods = new Goods();
    new Thread(()->{
      for(int i=0; i<10; i++){
        try{
          goods.produce();
        }catch(InterruptedException e){
          e.printStackTrace();
        }
      }
    },"PRODUCE1").start();
    new Thread(()->{
      for(int i=0; i<10; i++){
        try{
          goods.produce();
        }catch(InterruptedException e){
          e.printStackTrace();
        }
      }
    },"PRODUCE2").start();
    new Thread(()->{
      for(int i=0; i<10; i++){
        try{
          goods.consume();
        }catch(InterruptedException e){
          e.printStackTrace();
        }
      }
    },"CONSUME1").start();
    new Thread(()->{
      for(int i=0; i<10; i++){
        try{
          goods.consume();
        }catch(InterruptedException e){
          e.printStackTrace();
        }
      }
    },"CONSUME2").start();
  }
}

class Goods{
  private int number = 0;
  public synchronized void produce() throws InterruptedException{
    /*
    这里要用while判断,防止consume在经过if判断后都释放锁,然后一个produce生成一个产品后,两个consume轮流抢到锁,从1到-1,因为之前经过了一个if判断,没有再经过一次判断,所以要改为while
    真实情况是,线程被唤醒并抢到资源之后,其实还是满足 if 条件,也就是还需要继续阻塞,不应该被唤醒。
    */
    while(number != 0){ //判断
      this.wait();
    }
    number++;//干活
    System.out.println(Thread.currentThread().getName() + "Produce" + number);
    this.notifyAll(); //通知
  }
  public synchronized void consume() throws InterruptedException{
    while(number != 0){ //判断
      this.wait();
    }
    number--;
    System.out.println(Thread.currentThread().getName() + "Consume" + number);
    this.notifyAll(); //通知
  }
}

LockCondition的await() / signal()方法 即管程monitor

四个线程,一个产品
//生产消费共10轮
//生产消费共10轮
public class Demo{
  public static void main(String[] args){
    Goods goods = new Goods();
    new Thread(()->{
      for(int i=0; i<10; i++){
        try{
          goods.produce();
        }catch(InterruptedException e){
          e.printStackTrace();
        }
      }
    },"PRODUCE1").start();
    new Thread(()->{
      for(int i=0; i<10; i++){
        try{
          goods.produce();
        }catch(InterruptedException e){
          e.printStackTrace();
        }
      }
    },"PRODUCE2").start();
    new Thread(()->{
      for(int i=0; i<10; i++){
        try{
          goods.consume();
        }catch(InterruptedException e){
          e.printStackTrace();
        }
      }
    },"CONSUME1").start();
    new Thread(()->{
      for(int i=0; i<10; i++){
        try{
          goods.consume();
        }catch(InterruptedException e){
          e.printStackTrace();
        }
      }
    },"CONSUME2").start();
  }
}

class Goods{
  private int number = 0;
  private Lock lock = new ReentrantLock();
  private Condition condition = lock.newCondition();
  
  public void produce() throws InterruptedException{
    lock.lock();
    try{
      while(number != 0){
        condition.await();
      }
      number ++;
      System.out.println(Thread.currentThread().getName() + "Produce" + number);
      condition.signalAll();
    }finally{
      lock.unlock();
    }
  }
  
  public void consume() throws InterruptedException{
    lock.lock();
    try{
      while(number != 0){
        condition.await();
      }
      number ++;
      System.out.println(Thread.currentThread().getName() + "Consume" + number);
      condition.signalAll();
    }finally{
      lock.unlock();
    }
  }
}

使用信号量Semaphore实现生产者-消费者模式

Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源,在操作系统中是一个非常重要的问题,可以用来解决哲学家就餐问题。Java中的Semaphore维护了一个许可集,一开始先设定这个许可集的数量,可以使用acquire()方法获得一个许可,当许可不足时会被阻塞,release()添加一个许可。

在下列代码中,还加入了另外一个mutex信号量,维护生产者消费者之间的同步关系,保证生产者和消费者之间的交替进行

public class Demo{
  private static int count = 0;
  final Semaphore notFull = new Semaphore(10);
  final Semaphore notEmpty = new Semaphore(0);
  final Semaphore mutex = new Semaphore(1);
  
  public static void main(String[] args){
    Demo demo = new Demo();
    new Thread(demo.new Producer()).start();
    new Thread(demo.new Consumer()).start();
    new Thread(demo.new Producer()).start();
    new Thread(demo.new Consumer()).start();
    new Thread(demo.new Producer()).start();
    new Thread(demo.new Consumer()).start();
  }
  
  class Producer implements Runnable{
    public void run(){
      for(int i=0; i<10; i++){
        try{
          Thread.sleep(3000);
        }catch(InterruptedException e){
          e.printStackTrace();
        }
        try{
          notFull.acquire();
          mutex.acquire();
          count++;
          System.out.println(Thread.currentThread().getName() + "Produce" + count);
        }catch(InterruptedException e){
          e.printStackTrace();
        }finally{
          mutex.release();
          notEmpty.release();
        }
      }
    }
  }
  
  class Consumer implements Runnable{
    public void run(){
      for(int i=0; i<10; i++){
        try{
          Thread.sleep(3000);
        }catch(InterruptedException e){
          e.printStackTrace();
        }
        try{
          notEmpty.acquire();
          mutex.acquire();
          count--;
          System.out.println(Thread.currentThread().getName() + "Consume" + count);
        }catch(InterruptedException e){
          e.printStackTrace();
        }finally{
          mutex.release();
          notFull.release();
        }
      }
    }
  }
}

posted @ 2021-04-07 20:38  GladysChloe  阅读(227)  评论(0)    收藏  举报