面试手写生产者和消费者怎么办?我教你三招

首先了解什么算生产者消费者?

  1. 生产者生产产品,消费者消费,
  2. 有产品可以消费,无则不可以要生产,
  3. 生产一个消费一个

第一种就是最经典的synchronized版本

1.生产10个消费10个
2.这一种只需要在生产和消费方法加上锁就好了,然后生产完即通知消费即可,如果num为0则消费停止然后生产,num=1则反之
public class Test1 {
 public static void main(String[] args) {
  // TODO 自动生成的方法存根
  Data data=new Data();
         new Thread(()-> {
          for(int i=0;i<10;i++)
          try {
    data.product();
   } catch (InterruptedException e) {
    // TODO 自动生成的 catch 块
    e.printStackTrace();
   }
         },"手机店").start();
         new Thread(()->{
          for(int j=0;j<10;j++)
          try {
    data.consumer();
   } catch (InterruptedException e) {
    // TODO 自动生成的 catch 块
    e.printStackTrace();
   }
         },"顾客").start();
 }
}
class Data{
 private int num=0;
 public synchronized void product() throws InterruptedException {
  while(num!=0) {//不用if防止虚假唤醒
   this.wait();
  }
          num++;
          System.out.println(Thread.currentThread().getName()+"生产了"+num+"个手机");
          this.notify();
 }
 public synchronized void consumer() throws InterruptedException {
  while(num==0) {//不用if防止虚假唤醒
   this.wait();
  }
    num--;
          System.out.println(Thread.currentThread().getName()+"消费了"+num+"个手机");
          this.notify();
 }
}
手机店生产了1个手机
顾客消费了0个手机
手机店生产了1个手机
顾客消费了0个手机
手机店生产了1个手机
顾客消费了0个手机
手机店生产了1个手机
顾客消费了0个手机
手机店生产了1个手机
顾客消费了0个手机
手机店生产了1个手机
顾客消费了0个手机
手机店生产了1个手机
顾客消费了0个手机
手机店生产了1个手机
顾客消费了0个手机
手机店生产了1个手机
顾客消费了0个手机
手机店生产了1个手机
顾客消费了0个手机

第二种是用lock类的lock+Condition

1. 一样的加锁,但不是对于方法加锁,而是在生产核心和消费核心加锁,对比第一种肯定减少了锁的部分
2. 第二种优势就是可以选择唤醒的线程,比如这里是有个有钱的客人来消费,手机店1通知手机店2一起赚钱,可以选择通知具体线程唤醒
3. 在一的版本上改动
public class Test1 {
 public static void main(String[] args) {
  // TODO 自动生成的方法存根
  Data data=new Data();
         new Thread(()-> {
          for(int i=0;i<10;i++)
   data.product1();
         },"手机店1").start();
         new Thread(()-> {
             for(int i=0;i<10;i++)
    data.product2();
            },"手机店2").start();
         new Thread(()->{
          for(int j=0;j<10;j++)
   data.consumer();
         },"顾客").start();
 }
}
class Data{
 private int num=0;
 Lock lock=new ReentrantLock();//默认可重入的非公平锁
 Condition c1=lock.newCondition();
 Condition c2=lock.newCondition();
 Condition c3=lock.newCondition();
 public  void product1()  {
  lock.lock();
  try {
   while(num!=0) {//不用if防止虚假唤醒
    c1.await();
   }
     num++;
     System.out.println(Thread.currentThread().getName()+"生产了"+num+"个手机");
     c2.signal();
  } catch (InterruptedException e) {
   // TODO 自动生成的 catch 块
   e.printStackTrace();
  }finally {
   lock.unlock();
  }
 }
 public void product2()  {
  lock.lock();
  try {
   while(num!=1) {//不用if防止虚假唤醒
    c2.await();
   }
     System.out.println(Thread.currentThread().getName()+"生产了"+num+"个手机");
     num++;
     c3.signal();
  } catch (InterruptedException e) {
   // TODO 自动生成的 catch 块
   e.printStackTrace();
  }finally {
   lock.unlock();
  }
 }
 public  void consumer() {
  lock.lock();
  try {
   while(num!=2) {//不用if防止虚假唤醒
    c3.await();
   }
     System.out.println(Thread.currentThread().getName()+"消费了"+num+"个手机");
     num=0;
    c1.signal();
  } catch (Exception e) {
   // TODO 自动生成的 catch 块
   e.printStackTrace();
  }finally {
   lock.unlock();
  }
 }
}
手机店1生产了1个手机
手机店2生产了1个手机
顾客消费了2个手机
手机店1生产了1个手机
手机店2生产了1个手机
顾客消费了2个手机
手机店1生产了1个手机
手机店2生产了1个手机
顾客消费了2个手机
手机店1生产了1个手机
手机店2生产了1个手机
顾客消费了2个手机
手机店1生产了1个手机
手机店2生产了1个手机
顾客消费了2个手机
手机店1生产了1个手机
手机店2生产了1个手机
顾客消费了2个手机
手机店1生产了1个手机
手机店2生产了1个手机
顾客消费了2个手机
手机店1生产了1个手机
手机店2生产了1个手机
顾客消费了2个手机
手机店1生产了1个手机
手机店2生产了1个手机
顾客消费了2个手机
手机店1生产了1个手机
手机店2生产了1个手机
顾客消费了2个手机

第三种是最新版的,利用BlockingQueue解决了加锁问题

1. 用了volite保证了可见性,
2. BlockingQueue保证了原子性,共享资源的问题
3. 这里用一个促销活动为例子,促销卖手机,6秒后main老板叫停
public class Test1 {
 public static void main(String[] args) {
  // TODO 自动生成的方法存根
  Data data=new Data(new ArrayBlockingQueue<>(3));
         new Thread(()-> {
   try {
    System.out.println("手机店生产启动");
    data.product();
   } catch (InterruptedException e) {
    // TODO 自动生成的 catch 块
    e.printStackTrace();
   }
         },"手机店").start();
          new Thread(()->{
          System.out.println("顾客开始消费");
   try {
    data.consumer();
   } catch (InterruptedException e) {
    // TODO 自动生成的 catch 块
    e.printStackTrace();
   }
         },"顾客").start();
         
         try {
   TimeUnit.SECONDS.sleep(6);
    data.stop();
  } catch (InterruptedException e) {
   // TODO 自动生成的 catch 块
   e.printStackTrace();
  }
        System.out.println("6秒后活动结束大老板叫停了");
}
}
class Data{
 private volatile boolean num=true;
    private AtomicInteger atomicInteger=new AtomicInteger();
    BlockingQueue<String> blockingQueue=null;
    Data( BlockingQueue<String> blockingQueue){
     this.blockingQueue=blockingQueue;
     System.out.println("传进来的队列类型是"+blockingQueue.getClass().getName());
    }
 public  void product() throws InterruptedException  {
  String phonenum="";
  boolean flag;
    while(num) {//不用if防止虚假唤醒     
   phonenum=atomicInteger.incrementAndGet()+"";
   flag=blockingQueue.offer(phonenum,2,TimeUnit.SECONDS);//2秒添加一个
   if(flag) {
    System.out.println(Thread.currentThread().getName()+"生产了"+phonenum+"个手机");
   }else {
    System.out.println(Thread.currentThread().getName()+"生产了"+phonenum+"个手机失败");
   }
   TimeUnit.SECONDS.sleep(1);
  }
    System.out.println(Thread.currentThread().getName()+"生产厂家停止生产手机");
 }
 public  void consumer() throws InterruptedException {
  String result=null;
    while(num) {//不用if防止虚假唤醒     
   result=blockingQueue.poll(2,TimeUnit.SECONDS);//2秒添加一个
   if(null==result||result.equalsIgnoreCase("")) {
    num=false;
    System.out.println(Thread.currentThread().getName()+"拿不到手机了,不消费了");
    System.out.println();
    System.out.println();
    return;
   }
   System.out.println(Thread.currentThread().getName()+"消费了"+result+"个手机");
  }
 }
    public  void stop() {
   num=false;
    }
}

传进来的队列类型是java.util.concurrent.ArrayBlockingQueue
手机店生产启动
顾客开始消费
手机店生产了1个手机
顾客消费了1个手机
手机店生产了2个手机
顾客消费了2个手机
手机店生产了3个手机
顾客消费了3个手机
顾客消费了4个手机
手机店生产了4个手机
顾客消费了5个手机
手机店生产了5个手机
顾客消费了6个手机
手机店生产了6个手机
6秒后活动结束大老板叫停了
手机店生产厂家停止生产手机
顾客拿不到手机了,不消费了

总结

最好烂熟于心,能手写能讲原理,因为面试手写概率太大了,了解第三种将是面试的王牌,毕竟它涉及到了CAS,volite和BlockQueue的知识,将提高的价值

发布了105 篇原创文章 · 获赞 19 · 访问量 4967

猜你喜欢

转载自blog.csdn.net/jiohfgj/article/details/104864248
今日推荐