Java 模拟生产者与消费者

概要:

生产者与消费者是运用多线程协作,其中生产者不断生产产品,将这些产品放入指定的仓库(或大的容器);消费者是从仓库中取得产品。当仓库中产品放满时,生产者则需要停止生产;当仓库中没有产品时,消费者则需要停止消费,除非仓库中有产品。本实例介绍生产者不断采集鲜花放入花篮中,消费者不断从花篮中取出鲜花。

| |目录

技术要点

    运用多线程实现模拟生产者与消费者的技术要点如下:

  • 有synchronized的地方不一定有wait()与notify()方法,有wait()与notify()方法的地方必有synchronized,这是因为wait()方法和notify()方法不是属于线程类,而是每一个对象都具有的方法,而且,这两个方法都和对象锁有关,有锁的地方,必有synchronized。

  • 调用wait()方法前的判断最好用while,而不用if;while可以实现被唤醒的线程再次作条件判断,而if则只能判断一次。

  • 当仓库中产品数量为0时,调用wait()方法,使得当前消费者线程进入等待状态;当有新的产品时,调用notify()方法,唤醒等待的消费者线程;当仓库中的产品放满时,调用wait()方法,使得当前的生产者线程进入等待状态;当消费者取得产品时,调用notify()方法,唤醒等待的生产者线程。

代码实现

package net.xsoftlab.baike;
import java.util.Random;
class Flower {// 缓存数据,用于存与取数据的类(花篮)
	private final String[] data;
	private int tail; // 下一个放put的地方
	private int head; // 下一个放的flower地方
	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 flower) {// 放置鲜花
		System.out.println("当前线程" + Thread.currentThread().getName() + "放置鲜花"
				+ flower);
		try {
			while (count >= data.length) {
				wait();// 线程等待
			}
			data[tail] = flower;// 放置鲜花于数组
			tail = (tail + 1) % data.length;
			count++;
			notifyAll();
		} catch (Exception 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;
			count--;// 数组个数减一
			notifyAll();
		} catch (Exception e) {// 捕获异常
			System.out.println("取出鲜花出现错误:" + e.getMessage());
		}
		System.out.println("当前线程" + Thread.currentThread().getName() + "取出鲜花"
				+ flower);
		return flower;
	}
}
class ProductThread extends Thread {// 生产者线程类
	private final Random random;
	private final Flower flower;
	private static int id = 0; // 鲜花的流水号
	public ProductThread(String name, Flower flower, long seed) {// 构造方法进行初始化
		super(name);
		this.flower = flower;
		this.random = new Random(seed);
	}
	public void run() {// 实现Thread类的方法,启动线程
		try {
			while (true) {
				Thread.sleep(random.nextInt(1000));// 随机休眠
				String flowerID = ",鲜花流水号" + nextId();
				flower.put(flowerID);// 放置鲜花
			}
		} catch (Exception e) {// 捕获异常
		}
	}
	private static synchronized 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() {// 实现Thread类的方法,启动线程
		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) {// java程序主入口处
		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-3", flower, 103).start();
		new ConsumeThread("ConsumeThread-4", flower, 104).start();
		new ConsumeThread("ConsumeThread-5", flower, 105).start();
	}
}

程序解读

  1. 生产者线程ProductThread类在其构造方法中设置花篮和创建随机对象,run()方法根据为真的条件进行循环,每隔随机生成的毫秒后便生产一个鲜花产品,并调用put()方法将产品存入到花篮中。消费者线程ConsumeThread类在其构造方法中设置花篮和创建随机对象,run()方法根据为真条件进行循环,每隔随机生成的毫秒数调用take()方法从花篮中取走一个鲜花产品。

  2. Flower类负责存入与取走产品。put()方法负责存入产品。当花篮中的鲜花满时,当前线程进入等待状态,即当生产者线程在调用put()方法来存入产品时,如果发现篮中鲜花已满便不生产鲜花,生产者线程进入等待状态;如果篮中鲜花未满,当向篮中放鲜花时,调用notify()方法,唤醒等待的消费者线程。take()方法用来取走鲜花产品。如果发现篮中没有鲜花,则当前消费者线程进入等待状态;如果篮中有鲜花,还有可以放鲜花的地方,则在取走鲜花时,调用notify()方法,唤醒等待的生产者线程。

  3. 在类的main()主方法中建立一个大花篮,并为花篮关联了5个生产者线程和5个消费者线程,启动这些线程,便可以模拟生产者消费者。


评论关闭
评论 还能输入200
评论关闭
评论 还能输入200
资料加载中...
已关注 , 取消