线程学习笔记(十三)-模拟生产者消费者

生产者与消费者是运用多线程协作,其中生产者不断生产产品,将这些产品放入指定的仓库(或大的容器),消费者是从仓库中取得产品。当仓库中产品放满时,生产者则需要停止生产;当仓库中没有产品时,消费者则需要停止消费,除非仓库中有产品。本实例介绍生产者不断采集鲜花放入花篮中,消费者不断从花篮中取出鲜花。
运用多线程模拟生产者与消费者的技术要点如下:
有synchronized的地方不一定有wait()与notify()方法,有wait()与notify()方法的地方必有synchronized,这是因为wait()方法和notify()方法不属于线程类,而是每一个对象都有的方法,而且,这两个方法都和对象锁有关,有锁的地方,必有synchronized。
当仓库中产品数量为0时,调用wait()方法,使得当前消费者线程进入等待状态;当有新的产品时,调用notify()方法,唤醒等待的消费者线程;当仓库中的产品放满时,调用wait()方法,使得当前的生产者线程进入等待状态,当消费者取得产品时,调用notify()方法,唤醒等待的生产者线程。
package core;

import java.util.Random;

class Flower { // 缓存数据,用于存与取数据的类(花篮)
	private final String[] data;
	private int tail;// 字符串数组的尾部,用于放置put鲜花
	private int head;// 字符串数组的头部,用于取出take鲜花
	private int count;// 缓存内的鲜花数

	public Flower(int count) { // 创建并初始化鲜花数组
		this.data = new String[count];
		this.head = 0;
		this.tail = 0;
		this.count = 0;
	}

	public synchronized void put(String flowerId) {// 放置鲜花
		
		try {
			while (count >= data.length) {
				wait();
			}
			data[tail] = flowerId; // 把鲜花flowerId放在数组尾部
			tail = (tail + 1) % data.length; // 数组尾部下标加1,指向下一个空的位置
			count++; // 鲜花放置加1
			notifyAll();// 通知消费者取鲜花产品
			System.out.println("当前线程" + Thread.currentThread().getName() + "放置鲜花," + flowerId);
		} catch (InterruptedException e) {
			System.out.println("放置鲜花异常 " + e.getMessage());
		}
	}
	public synchronized String take(){
		String flower=null;
		try {
			while(count<=0){
				wait();
			}
			flower=data[head];//取出数组头部的鲜花产品
			head=(head+1)%data.length; //数组头部加1
			count--; //数组实际存储的鲜花产品减一
			notifyAll();//通知所有的生产者
		} catch (Exception e) {
			System.out.println("消费者取出鲜花异常 " + e.getMessage());
		}
		System.out.println("当前线程"+ Thread.currentThread().getName()+"取出鲜花"+flower);
		return flower;
	}
}

class ProductThread extends Thread {// 生产者线程类
	private Flower flower;
	private static int id = 0;  //鲜花流水号
	private Random random;

	public ProductThread(String name, Flower flower, long seed) {// 构造方法进行初始化
		super(name);
		this.flower = flower;
		this.random = new Random(seed);
	}

	public void run() {
		
			try {
				while (true) {
				Thread.sleep(random.nextInt(1000));// 随机休眠
				String flowerId = "鲜花流水号" + nextId();
				flower.put(flowerId);
				}
			} catch (InterruptedException e) {
				System.out.println("生产者放置鲜花异常 " + e.getMessage());
		}
	}

	public int nextId() {
		return id++;
	}
}

class ConsumeThread extends Thread {// 消费者线程
	private final Random random;
	private final Flower flower;
	public ConsumeThread(String name,Flower flower,long seed){
		super(name);
		this.flower=flower;
		this.random=new Random(seed);
	} 
	public void run(){
		try {
			while(true){
				String flower=this.flower.take();
				Thread.sleep(random.nextInt(1000));
			}
		} catch (Exception e) {
			System.out.println("消费者取出鲜花异常 " + e.getMessage());
		}
		}
	}
public class TextProductAndConsume {// 用多线程模拟生产者与消费者类
	public static void main(String[] args) {
		Flower flower = new Flower(5);// 创建可以放置5朵鲜花的花篮
		new ProductThread("ProductThread-1", flower, 001).start();
		new ProductThread("ProductThread-2", flower, 002).start();
		new ProductThread("ProductThread-3", flower, 003).start();
		new ProductThread("ProductThread-4", flower, 004).start();
		new ProductThread("ProductThread-5", flower, 005).start();
		new ConsumeThread("ConsumeThread-1",flower,101).start();
		new ConsumeThread("ConsumeThread-2",flower,102).start();
		new ConsumeThread("ConsumeThread-2",flower,103).start();
		new ConsumeThread("ConsumeThread-2",flower,104).start();
		new ConsumeThread("ConsumeThread-2",flower,105).start();

	}
}

源程序解读
(1)生产者线程ProductThread类在其构造方法中设置花篮和创建随机对象,run()方法根据为真的条件进行循环,每隔随机生成的毫秒后便生产一个鲜花产品,并调用put()方法将产品存入到花篮中。消费者线程ConsumeThread类在其构造方法中设置花篮和创建随机对象,run()方法根据为真条件进行循环,然后调用take()方法从花篮中取走一个鲜花产品,便再休眠随机生成的毫秒数。
(2)Flower类负责存入put与取走take产品。put()方法负责存入产品。当生产者线程在调用put()方法来存入产品时,如果发现篮中鲜花已满便生产鲜花,生产者线程进入等待状态;如果篮中鲜花未满,当向篮中放鲜花,然后调用notify()或者notifyAll()方法,唤醒等待的消费者线程。其中对于原书中把输出语句
System.out.println("当前线程" + Thread.currentThread().getName() + "放置鲜花," + flowerId);
放置在put()方法最开始是不对的,当前的生产者线程都还没有校验数组data是否有空位置就输入生产者已经放置鲜花的语句是不对的,本来可以放置5朵鲜花的花篮,最后产生了10个输出(5个正常输出,5个线程等待的非正常的输出),应该把该输出语句放置到notifyAll()方法后面。
take()方法用来取走鲜花产品。如果消费者在调用take()方法发现篮中没有鲜花,则当消费者线程进入等待状态;如果篮中有鲜花,还有可以放鲜花的地方,则在取走鲜花时,调用notify()方法,唤醒等待的生产者线程。
(3)在类的main()方法中建立一个有五朵鲜花的花篮,并为花篮关联5个生产者线程和5个消费者线程,启动这些线程,便可以模拟生产者消费者。




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值