37 多线程(九)——线程通信(生产者消费者问题的解决方法):管程法与信号灯法

线程通信

图片来源:尚学堂ppt

 

 

线程通信模型

管程法

案例一 没有加线程通信:

情景设置:工厂(生产者)生产馒头,仓库(缓冲器)存储馒头,商店(消费者)从仓库取走馒头,加线程安全,不加线程通信

 

  

package _20191206;
/**
 * 生产者消费者模型:管程法
 * @author TEDU
 *	情景设置:工厂(生产者)生产馒头,仓库(缓冲器)存储馒头,商店(消费者)从仓库取走馒头,加线程安全,不加线程通信
 */
public class CoTest01 {
	public static void main(String[] args) {
		Storage storage = new Storage();
		new Productor(storage).start();
		new Consumer(storage).start();
	}
}

//工厂:不止一家,多线程
class Productor extends Thread{
	private Storage stor;
	
	public Productor(Storage stor) {
		this.stor = stor;
	}
	public void run() {
		for(int i = 0; i < 200;i++) {
			stor.push(new SteamedBun(i));
		}
	}
}

//仓库:仓库就是容器,因为仓库有固定的大小,所以考虑用数组来做
class Storage{
	private SteamedBun[] buns = new SteamedBun[10];//仓库的大小是固定的
	private int count = 0;//仓库数组下标
	//入仓
	public synchronized void push(SteamedBun bun) {
		buns[count] = bun;
		System.out.println("存入第"+buns[count].getId()+"个馒头 count:"+count);
		count++;
	}
	//出仓
	public synchronized void pop() {
		count--;
		System.out.println("取出第"+buns[count].getId()+"个馒头 count:"+count);
	}
}

//商店:不止一家(多线程)
class Consumer extends Thread{
	private Storage stor;
	public Consumer(Storage stor) {
		this.stor = stor;
	}	
	public void run() {
		for(int i=0;i<200;i++) {
			stor.pop();
		}
	}
}

//商品:馒头
class SteamedBun {
	private int id;
	public SteamedBun(int id) {
		this.id = id+1;
	}
	public int getId() {
		return id;
	}
}

  

  

运行结果:

存入第1个馒头 count:0
存入第2个馒头 count:1
存入第3个馒头 count:2
存入第4个馒头 count:3
存入第5个馒头 count:4
存入第6个馒头 count:5
存入第7个馒头 count:6
存入第8个馒头 count:7
存入第9个馒头 count:8
存入第10个馒头 count:9
取出第10个馒头 count:9
取出第9个馒头 count:8
Exception in thread "Thread-0" 取出第8个馒头 count:7
取出第7个馒头 count:6
取出第6个馒头 count:5
取出第5个馒头 count:4
取出第4个馒头 count:3
取出第3个馒头 count:2
取出第2个馒头 count:1
取出第1个馒头 count:0
Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: -1
	at _20191206.Storage.pop(CoTest01.java:42)
	at _20191206.Consumer.run(CoTest01.java:55)
java.lang.ArrayIndexOutOfBoundsException: 10
	at _20191206.Storage.push(CoTest01.java:35)
	at _20191206.Productor.run(CoTest01.java:24)

 可以看到,取馒头时,取到仓库下标0后,再试图取已经没有可以取得馒头了,指针越界异常报出。

这个时候,我们需要引入wait与notify方法,在仓库存满时,停止生产馒头,仓库空时,停止取馒头。

 

案例二:加了线程通信

 在案例一的基础上,我们加上wait与notifyAll方法,通过判断生产与取货的时机,来停止生产与停止取货(wait()方法)以及继续生产继续取货(notifyAll()方法)。

  • this.wait() 停止当前线程

  • this.notifyAll() 激活其它线程
//入仓
	public synchronized void push(SteamedBun bun) {
		//何时停止存?调用wait
		if(count == buns.length) {//如果存到下标为仓库的大小,说明满了,停止存
			try {
				this.wait();//停止当前线程
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		buns[count] = bun;
		System.out.println("存入第"+buns[count].getId()+"个馒头 count:"+count);
		count++;
		//一旦开始存,就可以通知商家来取
		this.notifyAll();//激活其它线程
	}
	
	//出仓
	public synchronized void pop() {
		//何时出仓? 调用wait
		if(count == 0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		count--;
		System.out.println("取出第"+buns[count].getId()+"个馒头 count:"+count);
		//一旦开始取,仓库就不是满的,就要通知生产者生产
		this.notifyAll();
	}

  

  运行结果:

 信号灯法

适用:生产1个取1个的模式

情景设置:一个路口有一个斑马线信号灯,当信号灯红色,人停车走,当信号灯绿色,车停人走。

package _20191206;
/**
 * 生产者消费者模式:信号灯法
 * @author TEDU
 *	情景设置:一个路口有一个斑马线信号灯,当信号灯红色,人停车走,当信号灯绿色,车停人走。
 *	适用:生产1个取1个的模式
 */
public class CoTest02 {
	public static void main(String[] args) {
		//来一个信号灯
		SignalLight light = new SignalLight("斑马线信号灯",true);
		new Walker(light).start();
		new Car(light).start();
	}
}

//行人
class Walker extends Thread{
	private SignalLight light;
	public Walker(SignalLight light) {
		this.light = light;
	}
	
	public void run() {
		for (int i = 0; i < 10; i++) {
			light.forWalker();
		}
	}
}

//车
class Car extends Thread{
	private SignalLight light;
	public Car(SignalLight light) {
		this.light = light;
	}
	public void run() {
		for (int i = 0; i < 10; i++) {
			light.forCar();
		}
	}
}

//交通灯
class SignalLight{
	private boolean flag = true;
	//flag 值为 True 表示绿灯,行人走。值false表示红灯,车走。
	private String name;
	public SignalLight(String name,boolean b) {
		this.flag = b;
		this.name = name;
	}
	
	public synchronized void forWalker() {
		//查看信号灯
		if(!flag) {//红灯等待
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//绿灯走你
		System.out.println(name+":绿-行人正在过马路...");
		this.notifyAll();
		flag = !flag;
	}
	
	public synchronized void forCar() {
		//查看信号灯
		if(flag) {//绿灯等待
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//红灯走你
		System.out.println(name+":红-车正在驶过...");
		this.notifyAll();
		flag = !flag;
	}
}

  

结果:

斑马线信号灯:绿-行人正在过马路...
斑马线信号灯:红-车正在驶过...
斑马线信号灯:绿-行人正在过马路...
斑马线信号灯:红-车正在驶过...
斑马线信号灯:绿-行人正在过马路...
斑马线信号灯:红-车正在驶过...
斑马线信号灯:绿-行人正在过马路...
斑马线信号灯:红-车正在驶过...
斑马线信号灯:绿-行人正在过马路...
斑马线信号灯:红-车正在驶过...
斑马线信号灯:绿-行人正在过马路...
斑马线信号灯:红-车正在驶过...
斑马线信号灯:绿-行人正在过马路...
斑马线信号灯:红-车正在驶过...
斑马线信号灯:绿-行人正在过马路...
斑马线信号灯:红-车正在驶过...
斑马线信号灯:绿-行人正在过马路...
斑马线信号灯:红-车正在驶过...
斑马线信号灯:绿-行人正在过马路...
斑马线信号灯:红-车正在驶过...

  

posted @ 2019-12-06 14:22  Scorpicat  阅读(273)  评论(0编辑  收藏  举报