生产者消费者问题:
生产者消费者问题是一个经典的多线程协作问题。生产者负责生产产品,并将产品存放到仓库;消费者从仓库中获取产品并消费。当仓库满时,生产者必须停止生产,直到仓库有位置存放产品;当仓库为空时,消费者必须停止消费,直到仓库中有产品。
关键技术分析:
—用线程模拟生产者,在run()方法中不断的往仓库 中存放产品。
—用线程模拟消费者,在run()方法中不断的往仓库中获取产品。
—仓库类保存产品,当产品数量为0时,调用wait()方法,使得当前消费者进入线程等待状态,当有新产品调入时,调用notify方法,唤醒等待的消费者线程。当仓库满时,调用wait()方法,使得当前生产者线程进入等待状态,当有消费者获取产品时,调用notify()方法,唤醒等待的生产者线程。
—–停止生产者线程和消费者线程都通过设置标志位running为false来实现。
Product.java定义产品。Producer.java模拟生产者。Consumer.java模拟消费者,Warehouse.java类模拟仓库。TestProduct.java是一个测试程序。
package com.proandcom;
public class Product {
private String name;//产品名
public Product(String name) {
this.name = name;
}
public String toString(){
return "Product-"+name;
}
}
————————————————————————————
package com.proandcom;
class Producer extends Thread {
private Warehouse warehouse;//生产者存储产品的仓库
private static int produceName =0;//产品的名字
private boolean running=false;//是否需要结束线程的标志位
public Producer(Warehouse warehouse,String name) {
super(name);
this.warehouse = warehouse;
}
public void start(){
this.running=true;
super.start();
}
//把需要执行的代码都写在run()方法之中
public void run(){
Product product;
try {
while(running){
//生产并且存储产品
product=new Product((++produceName)+"");
this.warehouse.storageProduct(product);
sleep(300);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//停止生产者线程
public void stopProducer(){
//共享资源
synchronized (warehouse) {
this.running=false;//设置标志位为false
//通知等待仓库的线程
warehouse.notifyAll();
}
}
//生产者线程是否在运行
public boolean isRunning(){
return running;
}
}
——————————————————————————
package com.proandcom;
/*
* 消费者,采用线程,模拟消费者行为
*/
class Consumer extends Thread{
private Warehouse warehouse;//消费者获取产品的仓库
private boolean running =false;//是否需要线程结束的标志位
public Consumer(Warehouse warehouse,String name){
super(name);
this.warehouse=warehouse;
}
public void start(){
this.running=true;
super.start();
}
//将需要执行的代码都写在run方法中
public void run(){
Product product;
try {
while(running){
//从仓库中获取产品
product=warehouse.getProduct();
sleep(500);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//停止消费者线程
public void stopConsumer(){
//共享资源
synchronized (warehouse) {
//设置线程运行的标志位为false
this.running=false;
//通知等待仓库的线程
warehouse.notifyAll();
}
}
//消费者线程是否在运行
public boolean isRunning(){
return running;
}
}
————————————————————————————
package com.proandcom;
/**
* 产品的仓库类
* 内部采用数组来表示循环队列,以存放产品。
*/
public class Warehouse {
//仓库的容量
private static int CAPACITY = 11;
//仓库里的产品
private Product[] products;
//[front, rear)区间的产品是未被消费的
//当前仓库中第一个未被消费的产品的下标
private int front = 0;
//仓库中最后一个未被消费的产品的下标加1
private int rear = 0;
public Warehouse(){
this.products = new Product[CAPACITY];
}
public Warehouse(int capacity) {
this();
if (capacity > 0){
CAPACITY = capacity + 1;
this.products = new Product[CAPACITY];
}
}
/**
* 从仓库获取一个产品
*/
public Product getProduct() throws InterruptedException {
synchronized (this) {
//标志消费者线程是否还在运行
boolean consumerRunning = true;
//获取当前线程
Thread currentThread = Thread.currentThread();
if (currentThread instanceof Consumer){
consumerRunning = ((Consumer)currentThread).isRunning();
} else {
//非消费者不能获取产品
return null;
}
//如果仓库中没有产品,而且消费者线程还在运行,则消费者线程继续等待。
while ((front == rear) && consumerRunning){
wait();
consumerRunning = ((Consumer)currentThread).isRunning();
}
//如果消费者线程已经没有运行了,则退出该方法,取消获取产品
if (!consumerRunning){
return null;
}
//取当前未被消费的第一个产品
Product product = products[front];
System.out.println("Consumer[" + currentThread.getName()
+ "] getProduct: " + product);
//将当前未被消费产品的下标后移一位,如果到了数组末尾,则移动到首部。
front = (front + 1 + CAPACITY) % CAPACITY;
System.out.println("仓库中还没有被消费的产品数量:"
+ (rear + CAPACITY - front) % CAPACITY);
//通知其他等待线程
notify();
return product;
}
}
/**
* 向仓库存储一个产品
*/
public void storageProduct(Product product) throws InterruptedException {
synchronized (this) {
//标志生产者线程是否在运行
boolean producerRunning = true;
//获取当前线程
Thread currentThread = Thread.currentThread();
if (currentThread instanceof Producer){
producerRunning = ((Producer)currentThread).isRunning();
} else {
//不是生产者不能存储产品
return;
}
//如果最后一个未被消费产品与第一个未被消费的产品的下标紧挨着,
//则说明没有存储空间,如果没有存储空间而且生产者线程还在运行,则等待仓库释放产品。
while (((rear + 1) % CAPACITY == front) && producerRunning) {
wait();
producerRunning = ((Producer)currentThread).isRunning();
}
//如果生产者线程已经停止了,则停止产品的存储。
if (!producerRunning){
return;
}
//保存参数产品到仓库
products[rear] = product;
System.out.println("Producer[" + Thread.currentThread().getName()
+ "] storageProduct: " + product);
//将rear下标循环后移一位
rear = (rear + 1) % CAPACITY;
System.out.println("仓库中还没有被消费的产品数量:"
+ (rear + CAPACITY - front) % CAPACITY);
notify();
}
}
}
——————————————————————
package com.proandcom;
/**
* 测试生产者——消费者
* 支持多个生产者和消费者
*/
public class TestProduct {
public static void main(String[] args) {
//建立一个仓库,容量为10
Warehouse warehouse = new Warehouse(10);
//建立生产者和消费者
Producer producers1 = new Producer(warehouse, "producer-1");
Producer producers2 = new Producer(warehouse, "producer-2");
Producer producers3 = new Producer(warehouse, "producer-3");
Consumer consumers1 = new Consumer(warehouse, "consumer-1");
Consumer consumers2 = new Consumer(warehouse, "consumer-2");
Consumer consumers3 = new Consumer(warehouse, "consumer-3");
Consumer consumers4 = new Consumer(warehouse, "consumer-4");
//启动生产者和消费者线程
producers1.start();
producers2.start();
consumers1.start();
producers3.start();
consumers2.start();
consumers3.start();
consumers4.start();
//让生产者消费者程序运行1600ms
try {
Thread.sleep(1600);
} catch (InterruptedException e) {
e.printStackTrace();
}
//停止生产者和消费者的线程
producers1.stopProducer();
consumers1.stopConsumer();
producers2.stopProducer();
consumers2.stopConsumer();
producers3.stopProducer();
consumers3.stopConsumer();
consumers4.stopConsumer();
}
}