生产者消费者模式的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就是告知线程可以抢占对象内置锁了)。

原文地址:https://www.cnblogs.com/seanvon/p/4071756.html