Java producer-consumer(生产者/消费者模式)

来源:互联网 发布:做数据库有前途吗 编辑:程序博客网 时间:2024/06/06 01:59

       生产者消费者模式并不是GOF提出的23种设计模式之一,23种设计模式都是建立在面向对象的基础之上的,但其实面向过程的编程中也有很多高效的编程模式,生产者消费者模式便是其中之一,它是我们编程过程中最常用的一种设计模式。

     在java应用中,生产者/消费者模式的实现有以下五种:

     1:wait() / nofity()方法是基类Object的两个方法,也就意味着所有Java类都会拥有这两个方法,这样,我们就可以为任何对象实现同步机制。

     2.await()和signal()就是其中用来做同步的两种方法,它们的功能基本上和wait() / nofity()相同。

     3:BlockingQueue是JDK5.0的新增内容,它是一个已经在内部实现了同步的队列,实现方式采用的是我们第2种await() / signal()方法。

     4:PipedOutputStream和PipedInputStream是管道输出流和管道输入流,配合使用可以实现线程间通信。

     5:Semaphore除了控制资源的多个副本的并发访问控制,也可以使用二进制信号量来实现类似synchronized关键字和Lock锁的并发访问控制功能。

     源码地址:https://github.com/followwwind/javadesign

     

     下面对上面三种进行实现:

     

/** * @author wind */public interface Storage {    /**     * 仓库最大存储量     */    int MAX_SIZE = 100;    /**     * 生产num个产品     * @param num     */    void produce(int num);    /**     * 消费num个产品     * @param num     */    void consume(int num);}

     1.wait() / nofity()

/** * wait() / nofity()方法是基类Object的两个方法,也就意味着所有Java类都会拥有这两个方法,这样,我们就可以为任何对象实现同步机制。 * wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等等状态,让其他线程执行。 * notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。 * @author wind */public class StorageThree implements Storage{    /**     * 仓库最大存储量      */    private final int MAX_SIZE = 100;    /**     * 仓库存储的载体      */    private LinkedList<Object> list = new LinkedList<>();    /**     * 生产num个产品      * @param num     */    @Override    public void produce(int num) {        // 同步代码段        synchronized (list) {            // 如果仓库剩余容量不足            while (list.size() + num > MAX_SIZE) {                System.out.println("【要生产的产品数量】:" + num + "/t【库存量】:"                        + list.size() + "/t暂时不能执行生产任务!");                try {                    // 由于条件不满足,生产阻塞                    list.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            // 生产条件满足情况下,生产num个产品            for (int i = 1; i <= num; ++i) {                list.add(new Object());            }            System.out.println("【已经生产产品数】:" + num + "/t【现仓储量为】:" + list.size());            list.notifyAll();        }    }    /**     * 消费num个产品     * @param num     */    @Override    public void consume(int num) {        // 同步代码段        synchronized (list) {            // 如果仓库存储量不足            while (list.size() < num) {                System.out.println("【要消费的产品数量】:" + num + "/t【库存量】:"                        + list.size() + "/t暂时不能执行生产任务!");                try {                    // 由于条件不满足,消费阻塞                    list.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            // 消费条件满足情况下,消费num个产品            for (int i = 1; i <= num; ++i) {                list.remove();            }            System.out.println("【已经消费产品数】:" + num + "/t【现仓储量为】:" + list.size());            list.notifyAll();        }    }    public LinkedList<Object> getList() {        return list;    }    public void setList(LinkedList<Object> list) {        this.list = list;    }}

   2.await()和signal()

/** * 仓库类Storage实现缓冲区 * JDK5.0之后,Java提供了更加健壮的线程处理机制,包括同步、锁定、线程池等,它们可以实现更细粒度的线程控制。 * await()signal()就是其中用来做同步的两种方法,它们的功能基本上和wait() / nofity()相同, * 完全可以取代它们,但是它们和新引入的锁定机制Lock直接挂钩,具有更大的灵活性。 * 通过在Lock对象上调用newCondition()方法,将条件变量和一个锁对象进行绑定,进而控制并发程序访问竞争资源的安全 * @author wind */public class StorageOne implements Storage {    /**     * 仓库存储的载体      */    private LinkedList<Object> list = new LinkedList<>();    /**     *       */    private final Lock lock = new ReentrantLock();    /**     * 仓库满的条件变量      */    private final Condition full = lock.newCondition();    /**     * 仓库空的条件变量      */    private final Condition empty = lock.newCondition();    /**     * 生产num个产品      * @param num     */    @Override    public void produce(int num) {        // 获得锁        lock.lock();        // 如果仓库剩余容量不足        while (list.size() + num > MAX_SIZE) {            System.out.println("【要生产的产品数量】:" + num + "/t【库存量】:" + list.size()                    + "/t暂时不能执行生产任务!");            try {                // 由于条件不满足,生产阻塞                full.await();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        // 生产条件满足情况下,生产num个产品        for (int i = 1; i <= num; ++i) {            list.add(new Object());        }        System.out.println("【已经生产产品数】:" + num + "/t【现仓储量为】:" + list.size());        // 唤醒其他所有线程        full.signalAll();        empty.signalAll();        // 释放锁        lock.unlock();    }    /**     * 消费num个产品      * @param num     */    @Override    public void consume(int num) {        // 获得锁        lock.lock();        // 如果仓库存储量不足        while (list.size() < num) {            System.out.println("【要消费的产品数量】:" + num + "/t【库存量】:" + list.size()                    + "/t暂时不能执行生产任务!");            try {                // 由于条件不满足,消费阻塞                empty.await();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        // 消费条件满足情况下,消费num个产品        for (int i = 1; i <= num; ++i) {            list.remove();        }        System.out.println("【已经消费产品数】:" + num + "/t【现仓储量为】:" + list.size());        // 唤醒其他所有线程        full.signalAll();        empty.signalAll();        // 释放锁        lock.unlock();    }    public LinkedList<Object> getList() {        return list;    }    public void setList(LinkedList<Object> list) {        this.list = list;    }}
3.BlockingQueue

/** * 仓库类Storage实现缓冲区 * BlockingQueueJDK5.0的新增内容,它是一个已经在内部实现了同步的队列,实现方式采用的是我们第2await() / signal()方法。 * 它可以在生成对象时指定容量大小。它用于阻塞操作的是put()take()方法。 * put()方法:类似于我们上面的生产者线程,容量达到最大时,自动阻塞。 * take()方法:类似于我们上面的消费者线程,容量为0时,自动阻塞。 * @author wind */public class StorageTwo implements Storage {    /**     * 仓库存储的载体      */    private LinkedBlockingQueue<Object> list = new LinkedBlockingQueue<>(MAX_SIZE);    /**     * 生产num个产品      * @param num     */    @Override    public void produce(int num) {        // 如果仓库剩余容量为0        if (list.size() == MAX_SIZE) {            System.out.println("【库存量】:" + MAX_SIZE + "/t暂时不能执行生产任务!");        }        // 生产条件满足情况下,生产num个产品        for (int i = 1; i <= num; ++i) {            try {                // 放入产品,自动阻塞                list.put(new Object());            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println("【现仓储量为】:" + list.size());        }    }    /**     * 消费num个产品      * @param num     */    @Override    public void consume(int num) {        // 如果仓库存储量不足        if (list.size() == 0) {            System.out.println("【库存量】:0/t暂时不能执行生产任务!");        }        // 消费条件满足情况下,消费num个产品        for (int i = 1; i <= num; ++i) {            try {                // 消费产品,自动阻塞                list.take();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        System.out.println("【现仓储量为】:" + list.size());    }    public LinkedBlockingQueue<Object> getList() {        return list;    }    public void setList(LinkedBlockingQueue<Object> list) {        this.list = list;    }}

4.消费者类Consumer继承线程类Thread

public class Consumer extends Thread{    /**     * 每次消费的产品数量     */    private int num;    /**     * 所在放置的仓库     */    private Storage storage;    /**     * 构造函数,设置仓库     * @param storage     */    public Consumer(Storage storage) {        this.storage = storage;    }    public Consumer(int num, Storage storage) {        this.num = num;        this.storage = storage;    }    /**     * 线程run函数     */    @Override    public void run() {        consume(num);    }    /**     * 调用仓库Storage的生产函数     * @param num     */    public void consume(int num) {        storage.consume(num);    }    public int getNum() {        return num;    }    public void setNum(int num) {        this.num = num;    }    public Storage getStorage() {        return storage;    }    public void setStorage(Storage storage) {        this.storage = storage;    }}
5.生产者类Producer继承线程类Thread

public class Producer extends Thread{    /**     * 每次生产的产品数量     */    private int num;    /**     * 所在放置的仓库      */    private Storage storage;    /**     * 构造函数,设置仓库      * @param storage     */    public Producer(Storage storage) {        this.storage = storage;    }    public Producer(int num, Storage storage) {        this.num = num;        this.storage = storage;    }    /**     * 线程run函数      */    @Override    public void run() {        produce(num);    }    /**     * 调用仓库Storage的生产函数     * @param num     */    public void produce(int num) {        storage.produce(num);    }    public int getNum() {        return num;    }    public void setNum(int num) {        this.num = num;    }    public Storage getStorage() {        return storage;    }    public void setStorage(Storage storage) {        this.storage = storage;    }}

6.测试

/** * 测试类 * @author wind */public class Test {    private static Storage getStorage(String name){        Storage storage = null;        switch (name){            //await/signal            case "one" : storage = new StorageOne(); break;            //blockingqueue            case "two" : storage = new StorageTwo(); break;            //wait/nofity            default: new StorageThree(); break;        }        return storage;    }    public static void main(String[] args) {        // 仓库对象        Storage storage = getStorage("one");        // 生产者对象和产品生产数量        Producer p1 = new Producer(10, storage);        Producer p2 = new Producer(10, storage);        Producer p3 = new Producer(10, storage);        Producer p4 = new Producer(10, storage);        Producer p5 = new Producer(10, storage);        Producer p6 = new Producer(10, storage);        Producer p7 = new Producer(80, storage);        // 消费者对象和产品消费数量        Consumer c1 = new Consumer(50, storage);        Consumer c2 = new Consumer(20, storage);        Consumer c3 = new Consumer(30, storage);        // 线程开始执行        c1.start();        c2.start();        c3.start();        p1.start();        p2.start();        p3.start();        p4.start();        p5.start();        p6.start();        p7.start();    }}

阅读全文
0 0
原创粉丝点击