春节回了趟老家,又体验了一次流水席,因为桌席多,致使上菜慢,因而在等待间,总结了一下出菜流程的几个特色:java
1.有多个灶台,多个灶台都在同时作菜出来。面试
2.作出来的菜,会有专人用一个托盘端出来,每次端出来的菜(是同一个菜品)的数量不等。测试
3.因为端出来的菜可能不能知足全部的桌数,因此,端菜人可能会随机选择几桌(通常是就近原则,或者是主桌先端过去)上菜,其他的桌数继续等待后面的端菜人出来。this
以上3个条件,彻底就是一个生产者消费者的场景,因而,把生产者消费者先来实现一下,而后再分析如何才能更快的上菜 :)线程
首先,咱们把托盘给虚拟成一个资源池,表示这个托盘里是放菜的,当托盘里的菜大于1时,即有菜品被生产出来,端菜人就要端出去,当托盘里没有菜时,外面全部的桌席都要等待:对象
(须要特别注意的是,这个资源池只能有一个实例化对象,就像托盘的数量是固定的同样。)blog
public class ResourcePool { private int number = 0; public synchronized void producer(){ try { while(number==3){ this.wait(); } number++; System.out.println("producer: "+number); this.notifyAll(); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void consumer(){ try { while(number==0){ this.wait(); } number--; System.out.println("consumer: "+number); this.notifyAll(); } catch (InterruptedException e) { e.printStackTrace(); } } }
其实,咱们要有灶台,这个灶台是专门作菜的,作出来的菜,固然是所有放在了资源池(即托盘中),灶台是会有多个的,因此要继承thread类:继承
public class ResourceProduce extends Thread{ private ResourcePool rp; public ResourceProduce(ResourcePool rp) { this.rp = rp; } public void run() { rp.producer(); } }
托盘中有了菜,就得端出去了,给送到外面的桌席上去,因为桌席是多桌,因此,也要继承thread类:资源
public class ResourceConsumer extends Thread{ private ResourcePool rp; public ResourceConsumer(ResourcePool rp) { this.rp = rp; } public void run() { rp.consumer(); } }
这些基础的设施都准备好后,咱们的端菜人就出来了:it
public class ResourceUtil { public void resource(){ ResourcePool rp = new ResourcePool(); for (int i = 0; i < 3; i++) { new ResourceProduce(rp).start(); } for (int i = 0; i < 5; i++) { new ResourceConsumer(rp).start(); } } public static void main(String[] args) { ResourceUtil ru = new ResourceUtil(); ru.resource(); } }
咱们来看一下最后的输出结果:
当只有三个灶台,而桌席有5桌时,程序就等待下去了,因而,当咱们把灶台数改为5后,运行结果:
producer: 1 producer: 2 producer: 3 consumer: 2 producer: 3 consumer: 2 producer: 3 consumer: 2 consumer: 1 consumer: 0
经过上面的程序运行,若是想上菜速度快,仍是得加灶台,多加厨师,固然,这只是就这个场景简单的分析了一下,可能还会有更复杂的因素没考虑到,举这个例子的主要意思,是想让多多的理解一下生产者消费者模式,该模式咱们日常可能用原生的比较少,但其实使用的场景一直都在用,好比线程池,链接池,等等。因此,知其然也知其因此然也颇有必要,咱们接着就代码来讲明一下这个实现代码中的重点:
1.资源池有且只有一个。
2.synchronized,是锁对象,简单说一下:一个对象有且只有一把锁,当有多个synchronized方法或代码块都向该对象申请锁时,在同一时间,只会有一个线程获得该锁并运行,其它的就被阻塞了。
3.wait,是指该线程等待,wait有一个很重要的点,就是释放锁,上面也说了synchronized在同一时间只会有一个线程获得该锁并运行,因此,一旦wait后,就会释放锁,但当前线程等待下去,其它的线程再竞争这把锁。
4.notifyAll是指唤醒当前对象的全部等待的线程。
5.全部唤醒的线程会同时去竞争这把锁,可是JVM会随机选择一个线程并分配这把锁给该线程。
6.上面的synchronized wait notifyAll都是对一个对象进行操做,但这三个都是用在了资源池的类里面,因此,这也是资源池有且只能有一个的缘由。
后绪:至于生产者消费者能给咱们测试带来什么样的帮助,我暂时还没想到,但了解一下,出去面试时,有很大的可能性会被问到,有兴趣的,就看成一种知识储备吧。