0%

6.30生产者消费者

2017年6月30日 下午5:09

1
2
3
4
5
6
7
8
9
10
11
//馒头类
class SteamBread{
int id;//馒头编号

SteamBread(int id){
this.id = id;
}
public String toString(){
return "steamBread:"+id;
}
}
1
//装馒头的框,栈结构
class SyncStack{
	int index = 0;
	SteamBread[] stb = new SteamBread[6];//构造馒头数组,相当于馒头筐,容量是6
	
	//放入框中,相当于入栈
	public synchronized void push(SteamBread sb){
		while(index==stb.length){//筐满了,即栈满,
			try {
				this.wait();//让当前线程等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.notify();//唤醒在此对象监视器上等待的单个线程,即消费者线程
		stb[index] = sb;
		this.index++;
	}
	
	//从框中拿出,相当于出栈
	public synchronized SteamBread pop(){
		while(index==0){//筐空了,即栈空
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.notify();
		this.index--;//push第n个之后,this.index++,使栈顶为n+1,故return之前要减一
		return stb[index];
	}
}
1
//生产者类,实现了Runnable接口,以便于构造生产者线程
class Producer implements Runnable{
	SyncStack ss = null;
	Producer(SyncStack ss){
		this.ss = ss;
	}
	@Override
	public void run() {
		// 开始生产馒头
		for(int i=0;i<20;i++){
			SteamBread stb = new SteamBread(i);
			ss.push(stb);
			System.out.println("生产了"+stb);
			try {
				Thread.sleep(10);//每生产一个馒头,睡觉10毫秒
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
1
//消费者类,实现了Runnable接口,以便于构造消费者线程
class Consume implements Runnable{
	SyncStack ss = null;
	public Consume(SyncStack ss) {
		super();
		this.ss = ss;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<20;i++){//开始消费馒头
			SteamBread stb = ss.pop();
			System.out.println("消费了"+stb);
			try {
				Thread.sleep(100);//每消费一个馒头,睡觉100毫秒。即生产多个,消费一个
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}	
}
1
2
package thread;
//主类
public class ProduceConsume {
	public static void main(String[] args) {
		SyncStack ss = new SyncStack();//建造一个装馒头的框
		Producer p = new Producer(ss);//新建一个生产者,使之持有框
		Consume c = new Consume(ss);//新建一个消费者,使之持有同一个框
		Thread tp = new Thread(p);//新建一个生产者线程
		Thread tc = new Thread(c);//新建一个消费者线程
		tp.start();//启动生产者线程
tc.start();//启动消费者线程 } }

如何思考:

1. 根据我对算法的总结“先生活例子,然后提取概括,后算法实现”的思路
2. 我们先想这几个问题:
    1. 堆栈空了怎么办:消费者停止消费
    2. 堆栈满了怎么办:生产者停止生产
    3. 那么:我们既然要实现停止的功能,这时就得想到**线程**
    4. 因为:如果不用runnable,那么方法执行其不可控,只能一次执行完,或者出错一般的改写
3. 如果生产和消费同时进行怎么办:**sync**
4. 如何保证只用一个栈呢:**引用传参**
5. 容易忽略的问题:
    1. 既然生产和消费者都能停止,那么何时唤醒呢?
    2. 这个问题关键不是如何解决,而是注意到这个问题。
    3. **结论:等待 唤醒成对出现**
    4. **生产的时候唤醒消费,消费的时候唤醒生产**
    5. **生产暂停生产 ,消费暂停生产**
6. 实现:
    1. 先馒头 -> 栈 -> 生产者 -> 消费者 -> 总类

总结讨论:

1. 在这个实下中**一个消费者 ,一个生产者 ,一个栈**
2. 如果同一个消费者执行多次start(),同一个消费者也执行多次start()呢(图1)?


图1
这时还是共享一个资源
3. 如果多个消费者,多个生产者呢(图2)并且在生产者消费中将栈都声明为static

图2
这时还是共享一个资源
4. 如果多个消费者,多个生产者呢,但是却不将生产者消费中将栈声明为static
1. 这时,两个消费者使用不同的资源,两个消费也使用不同的资源
2. 但是,由于是引用类型,本质内存中只有一个资源空间
3. 程序就乱了!!!!!

注:在多线程中,虽然我们使用了多线程,但是其实一个具体的时刻还是只有一个线程运行,变得只是这些各自的多线程会走走停停,交互运行