一、什么是生产者与消费者

生产者与消费者是java并发环境下常见的设计模式,一个线程负责生产数据,一个线程负责消费数据,两个线程同时去操作这个变量,但是这是两个相互互斥的操作。

二、代码演示

1、使用synchronized来实现,必须结合wait()与notify()方法来互斥,代码如下:

/**
 * synchronized 版本生产者与消费者
 */
public class SynchronizedConsumerAndProducer {

    /**
     * 队列长度
     */
    private static final int MAX_LEN = 5;

    /**
     * 队列
     */
    private Queue<Integer> queue = new LinkedList<>();

    class Producer extends Thread {

        @Override
        public void run() {
            producer();
        }

        /**
         * 生产者
         */
        private void producer() {
            while (true) {
                synchronized (queue) {
                    while (queue.size() == MAX_LEN) { //队列已满
                        queue.notify();
                        System.out.println("当前队列已满");
                        try {
                            queue.wait();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    queue.add(1);
                    queue.notify();
                    System.out.println("生产者生成了一条数据,当前队列的长度为:" + queue.size());
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    class Consumer extends Thread {

        @Override
        public void run() {
            consumer();
        }

        private void consumer() {
            while (true) {
                synchronized (queue) {
                    while (queue.size() == 0) {
                        queue.notify();
                        System.out.println("当前队列为空");
                        try {
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.poll();
                    queue.notify();
                    System.out.println("消费者消费一条任务,当前队列的长度为:" + queue.size());
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        SynchronizedConsumerAndProducer consumerAndProducer = new SynchronizedConsumerAndProducer();
        Producer producer = consumerAndProducer.new Producer();
        Consumer consumer = consumerAndProducer.new Consumer();
        producer.start();
        consumer.start();
    }
}

这里为啥使用while循环判断,不使用if判断?

在使用if循环的时候会存在虚假唤醒,线程可以被唤醒,而不是被通知,中断或超时,即所谓的虚假唤醒,虽然正在实际操作中很少会发生,但是应用程序必须通过测试应该使用使线程被唤醒的条件来防范,并且如果条件不满足则继续等待,换句话说,等待应该出现的循环中,就像这样:

synchronized (obj) {
    while (<condition does not hold>) {
        obj.wait(timeout);
        // TODO
     }
 }

2、使用jdk的concurrent包下的Lock锁来实现,代码如下:

/**
 * 使用Lock锁的生产者与消费者
 */
public class ConditionConsumerAndProducer {

    /**
     * 队列长度
     */
    private static final int MAX_LEN = 5;

    /**
     * 队列
     */
    private Queue<Integer> queue = new LinkedList<>();

    /**
     * 获取lock锁
     */
    private final Lock lock = new ReentrantLock();

    private final Condition condition = lock.newCondition();

    /**
     * 生产者
     */
    class Producer extends Thread {

        @Override
        public void run() {
            producer();
        }

        private void producer() {
            while (true) {
                //加锁
                lock.lock();
                try {
                    while (queue.size() == MAX_LEN) {
                        System.out.println("当前队列已满");
                        try {
                            condition.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //添加数据
                    queue.add(1);
                    System.out.println("当前生产者,产生了一条数据,当前队列长度为:" + queue.size());
                    //唤醒其他线程来消费
                    condition.signal();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } finally {
                    //释放锁
                    lock.unlock();
                }
            }
        }
    }

    /**
     * 消费者
     */
    class Consumer extends Thread {

        @Override
        public void run() {
            consumer();
        }

        private void consumer() {
            while (true) {
                //加锁
                lock.lock();
                try {
                    while (queue.size() == 0) {
                        System.out.println("当前队列已空");
                        try {
                            condition.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.poll();
                    System.out.println("当前消费者,消费了一条数据,当前队列长度为:" + queue.size());
                    condition.signal();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } finally {
                    //释放锁
                    lock.unlock();
                }
            }
        }
    }

    public static void main(String[] args) {
        ConditionConsumerAndProducer consumerAndProducer = new ConditionConsumerAndProducer();
        Producer producer = consumerAndProducer.new Producer();
        Consumer consumer = consumerAndProducer.new Consumer();
        producer.start();
        consumer.start();
    }

}

3、使用BlockingQueue队列实现,一个线程put()数据,一个线程take()数据,代码如下:

/**
 * 使用BlockingQueue完成生产者与消费者
 */
public class BlockQueueConsumerAndProducer {

    private BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();

    class Producer extends Thread {

        @Override
        public void run() {
            producer();
        }

        private void producer() {
            while (true) {
                try {
                    /**
                     * 底层使用lock锁来实现
                     */
                    queue.put(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("生产者生产一条数据,当前队列长度为:" + queue.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class Consumer extends Thread {

        @Override
        public void run() {
            consumer();
        }

        private void consumer() {
            while (true) {
                try {
                    /**
                     * 底层使用lock锁来实现
                     */
                    queue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("消费者消费一条数据,当前队列长度为:" + queue.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
        for (int i = 0; i < 10; i++) {
            try {
                queue.put(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("queue长度为:" + queue.size());
        try {
            Integer take = queue.poll();
            System.out.println("获取到的元素为:" + take);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("执行take方法后,queue的长度为:" + queue.size());

        BlockQueueConsumerAndProducer consumerAndProducer = new BlockQueueConsumerAndProducer();
        Producer producer = consumerAndProducer.new Producer();
        Consumer consumer = consumerAndProducer.new Consumer();
        producer.start();
        consumer.start();
    }
}