生产者消费者的实现与思考

来源:互联网 发布:贝斯内录用什么软件 编辑:程序博客网 时间:2024/05/18 03:24

生产者消费者的实现与思考


生产者消费者的Java实现

生产者:负责生产消息,在缓冲区满后休眠;
消费者:负责消费消息,在缓冲区空后休眠;

两者的休眠何时唤醒?

1.生产者休眠,是因为缓冲区满,所以只要消费者进行了消费,那么缓冲区就会有新的空间,生产者就可以继续生产,故每次消费者消费以后都要试图唤醒生产者,无论生产者是否休眠。

2.消费者休眠,是因为缓冲区空,所以只要生产者进行了生成,那么缓冲区就会有新的消息,消费者就可以继续生产,故每次生产者生成以后都要试图唤醒消费者,无论消费者是否休眠。

使用wait()和notifyAll()实现

1.wait()、notify()、notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态。

一、如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
二、如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
三、如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。

其中wait方法有三个overload方法:
wait()
wait(long)
wait(long,int)
wait
方法通过参数可以指定等待的时长。如果没有指定参数,默认一直等待直到被通知。

《最简实例说明wait、notify、notifyAll的使用方法》http://www.cnphp6.com/archives/62258

2.synchronized:当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

五、以上规则对其它对象锁同样适用.

《java synchronized详解》http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html

缓冲区

import java.util.LinkedList;public class Storage {    private final int MAX_SIZE = 100; //此处为设置缓冲区大小    //消息的容器类,也可以选择队列这种先入先出的数据结构    private LinkedList<String> list = new LinkedList<String>();    //生产方法:由生产者调用    public void produce(String str){        //阻塞其他对list同步块的访问        synchronized(list){            //如果容器的大小等于预设的缓冲区大小,则为满            if(list.size()==MAX_SIZE){                try {                    System.out.println("满");                    //当前生产者线程交出list控制权,并进入等待                    list.wait();                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }else{                //如果缓冲区不满,则进行生产                list.push(str);                System.out.println(Thread.currentThread().getName()+"生产:"+str);                //通知所有在等待list权限的线程继续运行(唤醒消费者)此处有疑问?下方讨论                list.notifyAll();            }        }    }    //消费方法:由消费者调用    public void consume(){        //阻塞其他对list同步块的访问            synchronized(list){                //如果缓冲区为空                if(list.size()<1){                    try {                        System.out.println("空");                        //当前消费者交出list的权限,进入等待                        list.wait();                    } catch (InterruptedException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                    }                }else{                    //如果缓冲区不为空,消费                    String a  = list.pop();                    System.out.println(Thread.currentThread().getName()+"消费:"+a);                    //通知所有在等待list权限的线程继续运行(唤醒生产者)此处有疑问?下方讨论                    list.notifyAll();                }            }    }}

生产者

import java.util.Random;public class Producer extends Thread {    private Storage storage;    //生产者与消费者通过缓冲区发生联系    public Producer(Storage storage){        this.storage = storage;    }    public void run(){        while(true){//此处为无限生产,现实的情况可能有所不同,生产一个随机数                Random a = new Random();                storage.produce(a.nextInt(1000000)+"");        }    }}

消费者

public class Consumer extends Thread {    private Storage storage;    //生产者与消费者通过缓冲区发生联系    public Consumer(Storage storage){        this.storage = storage;    }    public void run(){        while(true){//此处为无限消费,现实的情况可能有所不同            storage.consume();        }    }}

Main方法

public class Main {    public static void main(String[] args) {        Storage storage = new Storage();          Producer p1 = new Producer(storage);          Producer p2 = new Producer(storage);         Producer p3 = new Producer(storage);         Producer p4 = new Producer(storage);         Consumer c1 = new Consumer(storage);          Consumer c2 = new Consumer(storage);          p1.start();          c1.start();          p2.start();          c2.start();          p3.start();          p4.start();      }}

运行结果

这里写图片描述

一个疑问

上面生产者与消费者的一个运行状态是

ProducerConsumer满,休眠,每次生成唤醒Consumer空,休眠,每次消费唤醒Producer


我的疑问:缓冲区满,生产者休眠,没毛病,因为继续生产也会被丢弃,和网络丢包一样。但是当缓冲区为空时,消费者为什么要休眠呢,消费者为不发送一个notifyAll然后继续自旋?

public void produce(String str){        synchronized(list){            if(list.size()>MAX_SIZE){                try {                    System.out.println("满");                    list.wait();                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }else{                list.push(str);                System.out.println(Thread.currentThread().getName()+"生产:"+str);            }        }    }    public void consume(){            synchronized(list){                if(list.size()<1){                        System.out.println("空");                        list.notifyAll();                }else{                    String a  = list.pop();                    System.out.println(Thread.currentThread().getName()+"消费:"+a);                }            }    }


一点分析:对比之前的方案,方案二可以理解为,生产者在库存满了之后会休眠,消费者要做的就是买买买,如果售罄了,消费者不停的要求补货(不休眠)….这样写也是线程安全的。

方案二的问题:消费者在缓冲区为空时候的会一直自旋不休眠,浪费资源,而且过多的空消费在现实中可能被认定为错误。这是我能想到最好的解释了。正如售罄时候,我们希望消费者试了一次发现售罄了,通知商家,然后回家等通知。

结论:消费者休眠为的是减少无效的消费,同理,生产者休眠是为了不丢弃资源(这个好理解一点)。
后来查证规定就是消费者不能空消费………..但是并不妨碍我们思考一波

使用await() / signal()实现(之后补充讨论)

《ReentrantLock与Condition》 http://www.tuicool.com/articles/6vANna

import java.util.LinkedList;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class Storage {    private final int MAX_SIZE = 100;    private LinkedList<String> list = new LinkedList<String>();    private final Lock lock = new ReentrantLock();      private final Condition full = lock.newCondition();      private final Condition empty = lock.newCondition();      public void produce(String str){        lock.lock();            if(list.size()>=MAX_SIZE){                try {                    System.out.println("满");                    full.await();                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }else{                list.push(str);                System.out.println(Thread.currentThread().getName()+"生产:"+str);                empty.signalAll();                lock.unlock();            }    }    public void consume(){        lock.lock();                if(list.size()<1){                    try {                        System.out.println("空");                        empty.await();                    } catch (InterruptedException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                    }                }else{                    String a  = list.pop();                    System.out.println(Thread.currentThread().getName()+"消费:"+a);                    full.signalAll();                    lock.unlock();                }    }}

参考:
http://www.cnblogs.com/alphablox/archive/2013/01/20/2868479.html
http://blog.csdn.net/monkey_d_meng/article/details/6251879


转载:http://blog.csdn.net/a298804870/article/details/53454978   谢谢博主分享

0 0