天天看点

Java并发编程与技术内幕:消费者-生产者模式研究

            消费者=生产者模式是Java并发编程中一个很好的应用实例,一般要求会如下:

 1、生产者仅仅在仓储未满时候生产,仓满则停止生产。

2、消费者仅仅在仓储有产品时候才能消费,仓空则等待。

3、当消费者发现仓储没产品可消费时候会通知生产者生产。

4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费。

      光看要求的话,应该不复杂。但是真正编程起来时,还是要考虑不少东西,本文将主要以四种方法来实现生产者-消费者模式:

一、wait/notify实现

    这是一种比较常规的实现方法,但是代码个人感觉有点复杂。并且缺点是不能完全实现仓储有产品马上就消费。代码如下:

package com.lin;

import java.util.LinkedList;
import java.util.List;

class Storehouse {
	// 仓库的容量
	private int capacity;
	// object当成是生产的商品
	private List<Object> list = new LinkedList<Object>();

	public Storehouse(int capacity) {
		this.capacity = capacity;
		System.out.println("当前仓库产品数量:" + list.size());
	}

	public int getCapacity() {
		return capacity;
	}

	public void setCapacity(int capacity) {
		this.capacity = capacity;
	}

	/**
	 * 生产的方法
	 * 
	 * @throws InterruptedException
	 */
	public void produrce(int num) throws InterruptedException {
		// 同步方法
		synchronized (list) {
			// 仓库还未满,且再生产num个产品不会超过仓库容量时可以生产产品
			while (list.size() + num > this.capacity) {
				// 仓库已满,或者放不下
				System.out.println("【仓库已无法再生产:" + num + "个产品】" + "当前仓库产品数量:" + list.size());
				list.wait();

			}

			System.out.println("【仓库还未满,生产:" + num + "个产品没有问题】" + "当前仓库产品数量:" + list.size());
			for (int i = 0; i < num; i++) {
				list.add(new Object());
			}
			list.notifyAll();
		}
	}

	/**
	 * 消费
	 * 
	 * @param num
	 * @throws InterruptedException
	 */
	public void consumer(int num) throws InterruptedException {
		// 同步方法
		synchronized (list) {
			// 仓库有没有num个产品可消费
			while (list.size() < num) {
				System.out.println("【仓库没有:" + num + "个产品可消费】" + "当前仓库产品数量:" + list.size());
				list.wait();
			}
			System.out.println("【仓库有:" + num + "个产品可消费】" + "当前仓库产品数量:" + list.size());
			for (int i = 0; i < num; i++) {
				list.remove(0);
			}
			list.notifyAll();
		}
	}
}

	class ProducerThread extends Thread {
		// 每次生产的产品数量
		private int num;

		// 所在放置的仓库
		private Storehouse storehouse;

		// 构造函数,设置仓库
		public ProducerThread(Storehouse storehouse, int num) {
			this.storehouse = storehouse;
			this.num = num;
		}

		public void run() {
			try {
				storehouse.produrce(num);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	class ConsumerThread extends Thread {
		// 每次生产的产品数量
		private int num;

		// 所在放置的仓库
		private Storehouse storehouse;

		// 构造函数,设置仓库
		public ConsumerThread(Storehouse storehouse, int num) {
			this.storehouse = storehouse;
			this.num = num;
		}

		public void run() {
			try {
				storehouse.consumer(num);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}

public class Test1 {

	public static void main(String[] args) {

		// 仓库对象
		Storehouse storage = new Storehouse(1000);

		// 生产者对象
		ProducerThread p1 = new ProducerThread(storage, 200);
		ProducerThread p2 = new ProducerThread(storage, 200);
		ProducerThread p3 = new ProducerThread(storage, 100);
		ProducerThread p4 = new ProducerThread(storage, 300);
		ProducerThread p5 = new ProducerThread(storage, 400);
		ProducerThread p6 = new ProducerThread(storage, 200);
		ProducerThread p7 = new ProducerThread(storage, 500);

		// 消费者对象
		ConsumerThread c1 = new ConsumerThread(storage, 500);
		ConsumerThread c2 = new ConsumerThread(storage, 200);
		ConsumerThread c3 = new ConsumerThread(storage, 800);

		// 线程开始执行
		c1.start();
		c2.start();
		c3.start();
		p1.start();
		p2.start();
		p3.start();
		p4.start();
		p5.start();
		p6.start();
		p7.start();

	}

}
           

输出结果:

当前仓库产品数量:0
【仓库没有:500个产品可消费】当前仓库产品数量:0
【仓库没有:800个产品可消费】当前仓库产品数量:0
【仓库还未满,生产:200个产品没有问题】当前仓库产品数量:0
【仓库没有:800个产品可消费】当前仓库产品数量:200
【仓库没有:500个产品可消费】当前仓库产品数量:200
【仓库还未满,生产:300个产品没有问题】当前仓库产品数量:200
【仓库有:500个产品可消费】当前仓库产品数量:500
【仓库没有:800个产品可消费】当前仓库产品数量:0
【仓库还未满,生产:200个产品没有问题】当前仓库产品数量:0
【仓库没有:800个产品可消费】当前仓库产品数量:200
【仓库有:200个产品可消费】当前仓库产品数量:200
【仓库没有:800个产品可消费】当前仓库产品数量:0
【仓库还未满,生产:200个产品没有问题】当前仓库产品数量:0
【仓库没有:800个产品可消费】当前仓库产品数量:200
【仓库还未满,生产:100个产品没有问题】当前仓库产品数量:200
【仓库没有:800个产品可消费】当前仓库产品数量:300
【仓库还未满,生产:400个产品没有问题】当前仓库产品数量:300
【仓库没有:800个产品可消费】当前仓库产品数量:700
【仓库已无法再生产:500个产品】当前仓库产品数量:700
           

结果不一定会和我一样

二、lock实现

    lock的实现其实和上面差不多,只不过还引入了lock,newCondition的条件,这是一个类似wait/notify的东西,用法也差不多。代码如下:

package com.lin;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Storehouse {
	// 仓库的容量
	private int capacity;
	// object当成是生产的商品
	private List<Object> list = new LinkedList<Object>();

	 // 锁  
    private final Lock lock = new ReentrantLock();  
  
    // 仓库满的条件变量  
    private final Condition full = lock.newCondition();  
  
    // 仓库空的条件变量  
    private final Condition empty = lock.newCondition();  
	
	public Storehouse(int capacity) {
		this.capacity = capacity;
		System.out.println("当前仓库产品数量:" + list.size());
	}

	public int getCapacity() {
		return capacity;
	}

	public void setCapacity(int capacity) {
		this.capacity = capacity;
	}

	/**
	 * 生产的方法
	 * 
	 * @throws InterruptedException
	 */
	public void produrce(int num) throws InterruptedException {
		try {
			lock.lock();
			// 仓库还未满,且再生产num个产品不会超过仓库容量时可以生产产品
			while (list.size() + num > this.capacity) {
				// 仓库已满,或者放不下
			   System.out.println("【仓库已无法再生产:" + num + "个产品】" + "当前仓库产品数量:" + list.size());
			   empty.await(); 
			}
			System.out.println("【仓库还未满,生产:" + num + "个产品没有问题】" + "当前仓库产品数量:" + list.size());
			for (int i = 0; i < num; i++) {
				list.add(new Object());
			}
			full.signalAll();
			empty.signalAll();
		} finally {
			lock.unlock();
		}
	}

	/**
	 * 消费
	 * 
	 * @param num
	 * @throws InterruptedException
	 */
	public void consumer(int num) throws InterruptedException {
		try {
			lock.lock();
			// 仓库有没有num个产品可消费
			while (list.size() < num) {
				System.out.println("【仓库没有:" + num + "个产品可消费】" + "当前仓库产品数量:" + list.size());
				full.await();
			}
			System.out.println("【仓库有:" + num + "个产品可消费】" + "当前仓库产品数量:" + list.size());
			for (int i = 0; i < num; i++) {
				list.remove(0);
			}
			empty.signalAll();
			full.signalAll();
		} finally {
			lock.unlock();
		}
	}
}

	class ProducerThread extends Thread {
		// 每次生产的产品数量
		private int num;

		// 所在放置的仓库
		private Storehouse storehouse;

		// 构造函数,设置仓库
		public ProducerThread(Storehouse storehouse, int num) {
			this.storehouse = storehouse;
			this.num = num;
		}

		public void run() {
			try {
				storehouse.produrce(num);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	class ConsumerThread extends Thread {
		// 每次生产的产品数量
		private int num;

		// 所在放置的仓库
		private Storehouse storehouse;

		// 构造函数,设置仓库
		public ConsumerThread(Storehouse storehouse, int num) {
			this.storehouse = storehouse;
			this.num = num;
		}

		public void run() {
			try {
				storehouse.consumer(num);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}

public class Test2 {

	public static void main(String[] args) {

		// 仓库对象
		Storehouse storage = new Storehouse(1000);

		// 生产者对象
		ProducerThread p1 = new ProducerThread(storage, 200);
		ProducerThread p2 = new ProducerThread(storage, 200);
		ProducerThread p3 = new ProducerThread(storage, 100);
		ProducerThread p4 = new ProducerThread(storage, 300);
		ProducerThread p5 = new ProducerThread(storage, 400);
		ProducerThread p6 = new ProducerThread(storage, 200);
		ProducerThread p7 = new ProducerThread(storage, 500);

		// 消费者对象
		ConsumerThread c1 = new ConsumerThread(storage, 500);
		ConsumerThread c2 = new ConsumerThread(storage, 200);
		ConsumerThread c3 = new ConsumerThread(storage, 800);

		// 线程开始执行
		c1.start();
		c2.start();
		c3.start();
		p1.start();
		p2.start();
		p3.start();
		p4.start();
		p5.start();
		p6.start();
		p7.start();

	}

}
           

输出结果:

当前仓库产品数量:0
【仓库没有:500个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库还未满,生产:200个产品没有问题】当前仓库产品数量:0
【仓库还未满,生产:100个产品没有问题】当前仓库产品数量:200
【仓库还未满,生产:500个产品没有问题】当前仓库产品数量:300
【仓库还未满,生产:200个产品没有问题】当前仓库产品数量:800
【仓库已无法再生产:300个产品】当前仓库产品数量:1000
【仓库已无法再生产:200个产品】当前仓库产品数量:1000
【仓库有:500个产品可消费】当前仓库产品数量:1000
【仓库有:200个产品可消费】当前仓库产品数量:500
【仓库还未满,生产:400个产品没有问题】当前仓库产品数量:300
【仓库没有:800个产品可消费】当前仓库产品数量:700
【仓库还未满,生产:300个产品没有问题】当前仓库产品数量:700
【仓库已无法再生产:200个产品】当前仓库产品数量:1000
【仓库有:800个产品可消费】当前仓库产品数量:1000
【仓库还未满,生产:200个产品没有问题】当前仓库产品数量:200
           

三、BlockingQueue实现

BlockingQueue是阻塞队列,可以实现入队阻塞、取数阻塞。对于生产者-消费者很适用,而且可以实现一有产品就可以马上就进行消费。不用像上面的两种方法都等到仓库生产足够的产品后才进行消费。而且代码量也不多,容易理解,代码如下:

package com.lin;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

class Storehouse {
	// 仓库的容量
	private int capacity;
	// 仓库存储的载体
	private BlockingQueue<Object> blockingQueue;
	// 当前个数
	private AtomicInteger curNum = new AtomicInteger(0);

	public Storehouse(int capacity) {
		this.capacity = capacity;
		this.blockingQueue = new ArrayBlockingQueue<Object>(capacity);
	}

	/**
	 * 生产的方法
	 * 
	 * @throws InterruptedException
	 */
	public void produrce(int num) {
		while (num + curNum.get() > capacity) {
			System.out.println("【仓库已无法再生产:" + num + "个产品】" + "当前仓库产品数量:" + curNum.get());
		}

		System.out.println("【仓库还未满,生产:" + num + "个产品没有问题】" + "当前仓库产品数量:" + blockingQueue.size());
		for (int i = 0; i < num; i++) {
			blockingQueue.add(new Object());
			curNum.incrementAndGet();
		}

	}

	/**
	 * 消费
	 * 
	 * @param num
	 * @throws InterruptedException
	 */
	public void consumer(int num) {
		while (num > curNum.get()) {
			System.out.println("【仓库没有:" + num + "个产品可消费】" + "当前仓库产品数量:" + blockingQueue.size());
		}

		System.out.println("【仓库有:" + num + "个产品可消费】" + "当前仓库产品数量:" + blockingQueue.size());
		for (int i = 0; i < num; i++) {
			try {
				blockingQueue.take();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			curNum.decrementAndGet();
		}

	}
}

class ProducerThread extends Thread {
	// 每次生产的产品数量
	private int num;

	// 所在放置的仓库
	private Storehouse storehouse;

	// 构造函数,设置仓库
	public ProducerThread(Storehouse storehouse, int num) {
		this.storehouse = storehouse;
		this.num = num;
	}

	public void run() {
			storehouse.produrce(num);
	}
}

class ConsumerThread extends Thread {
	// 每次生产的产品数量
	private int num;

	// 所在放置的仓库
	private Storehouse storehouse;

	// 构造函数,设置仓库
	public ConsumerThread(Storehouse storehouse, int num) {
		this.storehouse = storehouse;
		this.num = num;
	}

	public void run() {
			storehouse.consumer(num);
	}

}

public class Test3 {

	public static void main(String[] args) {
		// 仓库对象
		Storehouse storage = new Storehouse(1000);

		// 生产者对象
		ProducerThread p1 = new ProducerThread(storage, 200);
		ProducerThread p2 = new ProducerThread(storage, 200);
		ProducerThread p3 = new ProducerThread(storage, 100);
		ProducerThread p4 = new ProducerThread(storage, 300);
		ProducerThread p5 = new ProducerThread(storage, 400);
		ProducerThread p6 = new ProducerThread(storage, 200);
		ProducerThread p7 = new ProducerThread(storage, 500);

		// 消费者对象
		ConsumerThread c1 = new ConsumerThread(storage, 500);
		ConsumerThread c2 = new ConsumerThread(storage, 200);
		ConsumerThread c3 = new ConsumerThread(storage, 800);

		// 线程开始执行
		c1.start();
		c2.start();
		c3.start();
		p1.start();
		p2.start();
		p3.start();
		p4.start();
		p5.start();
		p6.start();
		p7.start();

	}

}
           

输出结果:

【仓库没有:500个产品可消费】当前仓库产品数量:0
【仓库没有:500个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库还未满,生产:200个产品没有问题】当前仓库产品数量:0
【仓库还未满,生产:200个产品没有问题】当前仓库产品数量:1
【仓库没有:500个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:0
【仓库没有:200个产品可消费】当前仓库产品数量:39
【仓库还未满,生产:300个产品没有问题】当前仓库产品数量:39
【仓库还未满,生产:100个产品没有问题】当前仓库产品数量:113
【仓库没有:800个产品可消费】当前仓库产品数量:0
【仓库还未满,生产:200个产品没有问题】当前仓库产品数量:165
【仓库没有:200个产品可消费】当前仓库产品数量:39
【仓库有:200个产品可消费】当前仓库产品数量:273
【仓库没有:500个产品可消费】当前仓库产品数量:39
【仓库没有:500个产品可消费】当前仓库产品数量:327
【仓库没有:500个产品可消费】当前仓库产品数量:327
【仓库没有:500个产品可消费】当前仓库产品数量:327
【仓库还未满,生产:400个产品没有问题】当前仓库产品数量:327
【仓库没有:800个产品可消费】当前仓库产品数量:327
【仓库没有:500个产品可消费】当前仓库产品数量:327
【仓库没有:800个产品可消费】当前仓库产品数量:479
【仓库没有:800个产品可消费】当前仓库产品数量:497
【仓库没有:500个产品可消费】当前仓库产品数量:516
【仓库有:500个产品可消费】当前仓库产品数量:516
【仓库还未满,生产:500个产品没有问题】当前仓库产品数量:506
【仓库没有:800个产品可消费】当前仓库产品数量:558
【仓库没有:800个产品可消费】当前仓库产品数量:558
【仓库没有:800个产品可消费】当前仓库产品数量:558
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:568
【仓库没有:800个产品可消费】当前仓库产品数量:567
【仓库没有:800个产品可消费】当前仓库产品数量:557
【仓库没有:800个产品可消费】当前仓库产品数量:607
【仓库没有:800个产品可消费】当前仓库产品数量:710
【仓库没有:800个产品可消费】当前仓库产品数量:727
【仓库没有:800个产品可消费】当前仓库产品数量:719
【仓库没有:800个产品可消费】当前仓库产品数量:719
【仓库没有:800个产品可消费】当前仓库产品数量:760
【仓库没有:800个产品可消费】当前仓库产品数量:760
【仓库没有:800个产品可消费】当前仓库产品数量:760
【仓库没有:800个产品可消费】当前仓库产品数量:760
【仓库没有:800个产品可消费】当前仓库产品数量:760
【仓库没有:800个产品可消费】当前仓库产品数量:760
【仓库没有:800个产品可消费】当前仓库产品数量:760
【仓库没有:800个产品可消费】当前仓库产品数量:760
【仓库没有:800个产品可消费】当前仓库产品数量:760
【仓库没有:800个产品可消费】当前仓库产品数量:760
【仓库没有:800个产品可消费】当前仓库产品数量:760
【仓库没有:800个产品可消费】当前仓库产品数量:769
【仓库有:800个产品可消费】当前仓库产品数量:835
           

四、管道实现

     待编程。。