zoukankan      html  css  js  c++  java
  • 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;
    	}
    }
    

      

    结果:

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

      

  • 相关阅读:
    Linux系统安全及应用
    Linux 10 进程和计划的管理任务
    Centos netdata 的安装及配置
    Centos nmon安装及使用
    Python 各种数据类型的了解(上)
    神奇的循环知识
    Pathon的基础知识
    初次遇见Python
    操作系统与编程语言的简介
    计算机硬件基础
  • 原文地址:https://www.cnblogs.com/Scorpicat/p/11994999.html
Copyright © 2011-2022 走看看