java多线程学习-简单生产者-消费者面试题
面试题
写一个固定容量同步容器,拥有put和get方法,以及getCount方法能够支持2个生产者线程以及10个消费者线程的阻塞调用
一,使用wait和notify/notifyAll来实现
import java.util.LinkedList; public class MyContainer1<T> { final private LinkedList<T> lists = new LinkedList<>(); final private int MAX = 10;//最多10个元素 private int count = 0; /**问题1: * 为什么下面对缓冲池的判断使用的是while而不是if? * 当缓冲池已满的话,再有生产者线程运行,线程就会陷入等待 * 举个栗子,假设当前缓冲池满,生产者线程A,B陆续运行 *如果是使用if的话: * A,B线程进入if方法体里,调用wait,统统陷入等待 * 等到有一个消费者在结束时进行唤醒,此时A,B均被唤醒,那么他们此时都不会在进行缓冲池是否满的判断了,他们都会直接到if语句的下一句继续运行 * 假设A先拿到锁,执行add操作,然后在结束时继续唤醒,当B拿到锁后,也是直接进行add操作,没有去判断此时缓冲池的情况,这样就会出问题 * 如果是使用while的话 * 在线程每次醒来后,都先去检查缓冲池的情况,若符合要求,才进行下一步操作,若不符合要求,还会继续等待 * */ public synchronized void put(T t) { while (lists.size() == MAX) {//想想这里为什么使用while而不是if? try { this.wait();//wait()大多数情况下都和while()在一起 } catch (InterruptedException e) { e.printStackTrace(); } } lists.add(t); ++count; this.notifyAll();//前面生产了,通知所有消费者线程进行消费 } /**问题2: * 为什么最后调用的是notifyAll()而不是notify()? * 因为在该对象上等待的可能不仅有消费者,还有生产者,若随机叫醒的一个又是生产者的话, * 若此时缓冲池已满,生产者就陷入等待,消费者便永远也不会被唤醒,整个程序陷入死锁 * 所以优先使用notifyAll() * */ public synchronized T get() { T t = null; while (lists.size() == 0) {//想想这里为什么使用while而不是if try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } t=lists.removeFirst(); --count; this.notifyAll();//前面消费了,通知所有生产者线程进行生产 return t; } }
二、使用Lock和Condition来实现
对比两种方式可以看出,Condition的方式可以更加精确的指定哪些线程被唤醒
package ThreadLearn.A005_Q; import java.util.LinkedList; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MyContainer2<T> { final private LinkedList<T> lists = new LinkedList<>(); final private int MAX = 10;//最多10个元素 private int count = 0; private Lock lock=new ReentrantLock(); private Condition producer =lock.newCondition(); private Condition consumer =lock.newCondition(); public void put(T t) { try { lock.lock(); while(lists.size()==MAX){ producer.await(); } lists.add(t); ++count; consumer.signalAll();//通知所有消费者线程进行消费 } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } public synchronized T get() { T t = null; try { lock.lock(); while(lists.size()==0){ consumer.await(); } t=lists.removeFirst(); --count; producer.signalAll();//通知生产者进行生产 } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } return t; } public static void main(String[] args) { MyContainer2<String> c=new MyContainer2<>(); //启动消费者线程 for (int i = 0; i <10 ; i++) { new Thread(()->{ for(int j=0;j<5;j++) System.out.println(c.get()); },"c"+i).start(); } try{ TimeUnit.SECONDS.sleep(2); }catch (InterruptedException e){ e.printStackTrace(); } //启动生产者者线程 for (int i = 0; i <2; i++) { new Thread(()->{ for(int j=0;j<25;j++) c.put(Thread.currentThread().getName()+" "+ j); },"p"+i).start(); } } }