对于经典的生产者和消费者模型,我做出以下理解以及代码的测试:
1.线程的等待与唤醒适用于生产速度和消费速度不匹配的情况,这时候构造一个类似容器的东西,一方未完成的时候,另一方在此等待。等到另一方完成的时候将对方唤醒继续执行;好处是可以解耦生产者和消费者的关系,系统易于维护。
案例分析,想要得到的效果:
当前存在一个牛奶生产者和一个牛奶消费者,生产者把生产好的牛奶放入盒子中,消费者从盒子中取出。盒子最大能容纳5瓶牛奶,生产一瓶牛奶需要2秒钟,消费者消耗一瓶牛奶需要3秒钟。
这个时间是我自己考虑的设定,存在固定的时间差,能够更容易的看轻每一步的执行结果。
其次,消费者只需要5瓶牛奶。不设定上限的话,就是程序就是无限循环下去。
而生产者只要有需要就会一直生产;
//盒子的类 public class Box { private int milk; public Box(int milk) { this.milk = milk; } public Box() { } public synchronized int getMilk() { if(milk==0){ try { System.out.println("没有牛奶,请等待"); //执行到此处的线程就进行等待 wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { milk--; System.out.println("取走第"+(milk+1)+"瓶牛奶"); //唤醒其他所有线程 notifyAll(); } return milk; } public synchronized void setMilk(int p) { if(milk==5){ System.out.println("牛奶已满,请等待"); try { //执行到此处的线程就进行等待 wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { milk++; System.out.println("放入第"+milk+"瓶牛奶"); //唤醒其他所有线程 notifyAll(); } } }
//顾客类 public class Custemer implements Runnable { private Box box; public Custemer(Box box) { this.box = box; } @Override public void run() { for (int i = 0; i < 6; i++) { box.getMilk(); System.out.println("消费者循环执行次数" + (i + 1)); try { //获取之后线程睡眠3秒 Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
//生产者类 public class Producer implements Runnable { private Box box; public Producer(Box box) { this.box = box; } @Override public void run() { while (true){ box.setMilk(1); System.out.println("正在生产牛奶,需2秒钟"); try { //以线程睡眠代表生产时间 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
//主函数测试类 public class Test01 { public static void main(String[] args) { Box b = new Box(); Producer p = new Producer(b); Custemer c = new Custemer(b); Thread tp = new Thread(p); Thread tc = new Thread(c); //启动线程 tc.start(); tp.start(); } }
1.执行结果的逐步分析如下图:
总结:
wait()方法属于Object类,并不是任何一个线程属性,可以理解为一个标识,线程看到这个标识了就会自己进行等待。跟睡眠不同,睡眠是自己让自己睡眠,并且知道自己要睡多少,所以知道什么时候醒来。但是看到等待,却不知道自己要等多久,所以需要别人去唤醒他。
notify以及notifyall都可以唤醒线程。唤醒的时候并不能指定唤醒谁,要么随机唤醒一个,要么唤醒所有,建议多数情况下使用唤醒所有。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)