JAVA生产者消费者的实现

转载地址:http://www.cnblogs.com/zhangfei/p/5188872.html


春节回了趟老家,又体验了一次流水席,由于桌席多,导致上菜慢,于是在等待间,总结了一下出菜流程的几个特点:

1.有多个灶台,多个灶台都在同时做菜出来。

2.做出来的菜,会有专人用一个托盘端出来,每次端出来的菜(是同一个菜品)的数量不等。

3.由于端出来的菜可能不能满足所有的桌数,所以,端菜人可能会随机选择几桌(一般是就近原则,或者是主桌先端过去)上菜,其余的桌数继续等待后面的端菜人出来。

以上3个条件,完全就是一个生产者消费者的场景,于是,把生产者消费者先来实现一下,然后再分析如何才能更快的上菜 :)

首先,我们把托盘给虚拟成一个资源池,表示这个托盘里是放菜的,当托盘里的菜大于1时,即有菜品被生产出来,端菜人就要端出去,当托盘里没有菜时,外面所有的桌席都要等待:

(需要特别注意的是,这个资源池只能有一个实例化对象,就像托盘的数量是固定的一样。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ResourceProduce extends Thread{
     
     private ResourcePool rp;
 
     public ResourceProduce(ResourcePool rp) {
         this .rp = rp;
     }
 
     public void run() {
         rp.producer();
     }
 
}

 托盘中有了菜,就得端出去了,给送到外面的桌席上去,由于桌席是多桌,所以,也要继承thread类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ResourceConsumer extends Thread{
     
     private ResourcePool rp;
 
     public ResourceConsumer(ResourcePool rp) {
         this .rp = rp;
     }
 
     public void run() {
         rp.consumer();
     }
 
}

 这些基础的设施都准备好后,我们的端菜人就出来了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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后,运行结果:

1
2
3
4
5
6
7
8
9
10
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都是对一个对象进行操作,但这三个都是用在了资源池的类里面,所以,这也是资源池有且只能有一个的原因。

 

后绪:至于生产者消费者能给我们测试带来什么样的帮助,我暂时还没想到,但了解一下,出去面试时,有很大的可能性会被问到,有兴趣的,就当作一种知识储备吧。

点亮测试人生!QQ:408129370

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值