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();
        }

    }
}

 

posted @ 2020-03-26 19:48  pathjh  阅读(416)  评论(0编辑  收藏  举报