生产者消费者消费者模式之通过Condition唤醒(和wait、notify模式做对比)

下面的代码有详细的注释,通过Condition的方式实现更细粒度的通知!

仔细阅读下面的代码,对多线程的理解有很大的帮助

package com.lyzx.concurrent.lock;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ProducerConsumer {

    public static void main(String[] args) {

        Box box = new Box();
        Runnable in = ()->{
            for(int i=0;i<10;i++){
                box.put("P"+i);
            }
        };

        Runnable out = ()->{
            for(int i=0;i<5;i++){
                box.get();
            }
        };

        for(int i=0;i<10;i++){
            new Thread(out,"Consume_"+i).start();
            if(i < 5){
                new Thread(in,"Producer_"+i).start();
            }
        }

    }
}

class Box{
    private Lock lock = new ReentrantLock();
    private Queue q = new LinkedList<>();


    /**
     * 这个Condition对象相当于一个Object对象,await方法让线程在此对象上等待
     * 通过signalAll方法唤醒在这个对象上等待的所有线程
     */

//  private Condition cond = lock.newCondition();

    /**
     * 通过ReentrantLock类的对象lock,获取一个"条件"
     * 每次只让一种成员在这个对象上面等待
     * 如所有的消费者都在 consumer 上等待,当生产者生产满了后直接调用 consumer.signalAll()
     * 就可以唤醒所有的消费者了,不存在唤醒消费者让其再次等待的过程,所以理论上效率会更高,
     *
     * 记住Condition的await()方法会释放锁,此时它是持有锁的,所以才能释放锁,如果使用普通对象的
     * wait方法时会抛出java.lang.IllegalMonitorStateException 这个异常
     * 如下使用oConsumer和oProducer对象代替Con代替Condition的方法时会抛出异常
     *
     * 所以说通过Synchronized修饰方法或者使用Synchronized代码块是实现不了这样细粒度的通知的
     *
     */

    private Condition consumer =  lock.newCondition();
    private Condition producer = lock.newCondition();

//    private Object oConsumer = new Object();
//    private Object oProducer = new Object();

    private final int MAX = 10;
    private int size = 0;

    /**
     * 通过consumer和producer两个Condition即两个对象
     *
     * @return
     */
    public T get(){
        try{
            lock.lock();
            String c = Thread.currentThread().getName();
            while(size == 0){
                System.out.println(c+" get在等待!");
                /**
                 * 1、等待的第一种形式,所有的成员(生产者和消费者)都在这个对象上等待
                 */
                //cond.await();

                /**
                 * 2、等待的第二种形式
                 * 只让消费者线程在这个对象上等待,这个方法由消费者线程等待
                 * 当生产者线程生产满容器后就直接唤醒所有的消费者线程,不用唤醒生产者线程(相比于上一种形式)
                 * 理论上效率会高一些
                 */
                consumer.await();
//                oConsumer.wait();
            }

            size--;

            /**
             * 1、唤醒的第一种形式,这里唤醒的是所有的成员,包括生产者和消费者
             * 需要注意的是当唤醒消费者时(这个方法本身有消费者线程调用),等待的消费者会
             * 做再一次的判断(通过while消除虚假唤醒),这次当然条件依然为true,所以会继续等待
             */
//            cond.signalAll();


            /**
             * 2、第二种唤醒形式
             * 这里只唤醒生产者进程,即在producer这个对象上等待的线程
             * 理论上效率会比上一种形式高
             */
            producer.signalAll();

//            oProducer.notifyAll();
            T t = q.poll();
            System.out.println(c+" 取走了 "+t);
            return t;
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }finally{
            lock.unlock();
        }
    }

    public void put(T t){
        try{
            lock.lock();
            String c = Thread.currentThread().getName();

            while(size == MAX){
                System.out.println(c+" put在等待!");
//                cond.await();
                producer.await();
//                oProducer.wait();
            }

            q.add(t);
            System.out.println(Thread.currentThread().getName()+" 放入了 "+t);
            size++;

//            cond.signalAll();
            consumer.signalAll();
//            oConsumer.notifyAll();
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}


你可能感兴趣的:(Java)