• 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();
            }
    
        }
    }
  • 相关阅读:
    window 删除文件提示指定的文件名无效或太长
    glib-2.40编译安装
    《Android权威编程指南(The Big Nerd Ranch Guide)(第二版)》12.4挑战练习
    Kotlin中when表达式的使用:超强的switch(KAD 13)
    Kotlin将Realm提升到更高层次
    Kotlin中的“忍者”函数 —— 理解泛型的能力(KAD 12)
    Kotlin中功能操作与集合(KAD 11)
    Kotlin的数据类:节省很多行代码(KAD 10)
    在Android中用Kotlin的Anko运行后台任务(KAD 09)
    Kotlin的扩展函数:扩展Android框架(KAD 08)
  • 原文地址:https://www.cnblogs.com/pathjh/p/12576857.html
Copyright © 2020-2023  润新知