首页 > 代码库 > 生产者消费者模式的java实现(实现一)

生产者消费者模式的java实现(实现一)

在多线程以及并发工具类中,常用的一种思想就是生产者消费者模式,生产者负责生产物品,将物品放到传送带,消费者负责获取传送带的物品,消费物品。现在只考虑最简单的情况,传送带上只允许放一个物品。

1、传送带为空,则允许生产者放置物品,否则不许放(生产者线程wait)。

2、生产者放置完物品后,通知消费者可以拿了(线程通信,notify 或者notifyAll)。

2、传送带不空,则允许消费者拿物品,否则不许拿(消费者线程wait)。

3、消费者拿走物品后,通知生产者可以继续生产(线程通信,notify 或者notifyAll)。

 

package com.smikevon.concurrent;import java.util.Random;/** * @description: 生产者消费者模式,通过wait和notify方式来实现 * @date       : 2014年9月12日 上午11:39:31 */public class ProducerConsumer_01 {    public static void main(String[] args) {        Drop drop = new Drop();        new Thread(new Producer(drop)).start();        new Thread(new Consumer(drop)).start();    }}/** * @description: 传送带,只能有一个物品(message)在上面 * @date       : 2014年9月12日 下午12:03:08 */class Drop{    private String message;    private boolean empty = true;    public synchronized String take() throws InterruptedException{        while(empty){            wait();        }        empty = true;        notifyAll();        return message;    }    public synchronized void put(String message) throws InterruptedException{        while(!empty){            wait();        }        this.message = message;        empty = false;        notifyAll();    }}/** * * @description: 生产者随机放入字符串 * @date       : 2014年9月12日 上午11:53:27 */class Producer implements Runnable {    private Drop drop;    public Producer(Drop drop) {        this.drop = drop;    }    public void run() {        String[] messages = {            "我是",            "一名程序员",            "我很骄傲",            "也很自豪",            "爱岗敬业",            "勤劳奉献",            "无怨无悔",            "奉献青春",            "希望通过学习",            "提升",            "自己",            "done",        };        try {            for(int i=0;i<messages.length;i++){                System.out.format("%s: 放入信息-----------%s %n",Thread.currentThread().getName(),messages[i]);                drop.put(messages[i]);                Thread.sleep(new Random(System.currentTimeMillis()).nextInt(5000));            }        } catch (InterruptedException e) {            e.printStackTrace();        }    }}/** * @description: 消费者,有字符串,就取出来 * @date       : 2014年9月12日 下午12:02:35 */class Consumer implements Runnable{    private Drop drop;    Consumer(Drop drop){        this.drop = drop;    }    public void run() {        try {            String message = "";            System.out.println(drop);            while(!(message = drop.take()).equals("done")){                System.out.format("%s: 取出信息-------------%s %n",Thread.currentThread().getName(),message);                Thread.sleep(new Random(System.currentTimeMillis()).nextInt(5000));            }        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

在线程wait的代码处都采用了循环测试条件(专业名称叫条件谓词),如下

while(!empty){            wait();        }

  是因为在另一个线程notifyAll,唤醒本线程后,无法确认此时就一定满足测试条件。两个线程不会有问题,但是在更多线程的时候就会出问题,因为你无法确认自己就是被生产者线程唤醒的,可能在唤醒之前已经有其他线程改变过状态变量(本类就是Drop里的message),这样就会出现异常,因此需要在被唤醒后立马测试下条件是否已经满足。将一致性保障交给竞态条件(notifyAll就是等待的线程竞争获取对象的锁)是不良的变成习惯。

  下面一节(实现二)会介绍ReenrantLock,里面将等待与唤醒条件进行了细分,让你可以赋予条件真实的意义,而不只将意义绑定到锁的持有上(wait是告诉本线程挂起 和 notify就是告知线程可以抢占对象内置锁了)。

生产者消费者模式的java实现(实现一)