Java 多线程之生产者消费者

来源:互联网 发布:传奇千里传音数据 编辑:程序博客网 时间:2024/06/07 09:05

一、简介

在多线程学习中经常会遇到的一个经典问题便是生产者消费者模式,它描述了有一款作为仓库的缓冲区,生产者将产品放入产库,消费者从仓库将产品拿走的一个过程。解决生产者/消费者问题的方法可分为两类:(1)采用某种保护机制使生产者和消费者之间同步;(2)在生产者和消费者之间建立一个管道。第一种方式有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式。第二种管道缓冲区不易控制,被传输数据对象不易于封装等,实用性不强。因此本文只介绍同步机制实现的生产者/消费者问题。
那么同步问题核心在于:如何保证临界资源在多线程中被至多一个线程操作,常用的方式是加锁和信号机制。在java中提供了三种同步的方法
(1)wait() / notify()方法
(2)await() / signal()方法
(3)BlockingQueue阻塞队列方法

二、研究内容

本文主要对 await() / signal()方法进行学习研究
在JDK5.0之后,Java提供了更加健壮的线程处理机制,包括同步、锁定、线程池等,它们可以实现更细粒度的线程控制。await()和signal()就是其中用来做同步的两种方法,它们的功能基本上和wait() / nofity()相同,完全可以取代它们,但是它们和新引入的锁定机制Lock直接挂钩,具有更大的灵活性。通过在Lock对象上调用newCondition()方法,将条件变量和一个锁对象进行绑定,进而控制并发程序访问竞争资源的安全。下面来看代码:

package test;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 = 20; //仓库的总长度    private Lock lock = new ReentrantLock();//锁    private Condition full = lock.newCondition();//与锁关联的满条件变量    private Condition empty = lock.newCondition();//与锁关联的空条件变量    private LinkedList<String> list = new LinkedList<String>();    public void produce(int i) {        lock.lock();        while (list.size() == MAX_SIZE) {            try {                System.out.println("仓库已满等待消费者取走商品");                full.await(); // 队列满的时候等待消费者取走货物            } catch (InterruptedException e) {                e.printStackTrace();            }        }        list.add("产品" + i);        empty.signal();        lock.unlock();    }    public void consume() {        lock.lock();        while (list.isEmpty()) {            try {                System.out.println("仓库已空等待生产者生产商品");                empty.await();// 队列空的时候等待生产者生产产品            } catch (InterruptedException e) {                e.printStackTrace();            }        }        System.out.println(list.getFirst());        list.removeFirst();        ConsumerThread.CONSUMER_NUM++;        full.signal();        lock.unlock();    }}/** * 生产者类 * */public class ProduceThread extends Thread{    public ProduceThread(Storage storage) {        this.storage = storage;    }    public static final int PREPARE_NUM = 50;//生产者准备生产的产品    public Storage getStorage() {        return storage;    }    public void setStorage(Storage storage) {        this.storage = storage;    }    private Storage storage;    @Override    public void run() {        for(int i = 0; i < PREPARE_NUM; i++){            storage.produce(i);        }    }}package test;/** * 消费者类 * */public class ConsumerThread extends Thread{    public static int CONSUMER_NUM = 0;    public ConsumerThread(Storage storage) {        this.storage = storage;    }    public Storage getStorage() {        return storage;    }    public void setStorage(Storage storage) {        this.storage = storage;    }    private Storage storage;    @Override    public void run() {        while(true){            storage.consume();            //当所有产品生产结束时退出消费            if (CONSUMER_NUM == ProduceThread.PREPARE_NUM)             {                break;            }        }    }}package test;public class test {    public static void main(String[] args) {        Storage storage = new Storage();        ProduceThread produceThread = new ProduceThread(storage);        ConsumerThread consumerThread = new ConsumerThread(storage);        produceThread.start();        consumerThread.start();        try {            produceThread.join();            consumerThread.join();        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}

输出如下:
仓库已满等待消费者取走商品
产品0
产品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
产品32
产品33
产品34
产品35
产品36
产品37
产品38
产品39
产品40
产品41
仓库已空等待生产者生产商品
产品42
产品43
产品44
产品45
产品46
产品47
产品48
产品49

刚好最近公司有一个项目,需要做一个数据库的数据比对分析差错功能,在两个库的处理上面一个使用直接从数据库获取数据,另一个由于网络无法直连只能通过文件传输的方式,将数据库的内容以公司内部规范的格式生成文件,程序以读取文件的方式加载另一个库的数据,来进行数据的比对分析。为了提高效率,在数据加载和数据处理的核心模块设计上,我打算使用生产者消费者模式,两个生产者线程分别生产数据库的模型数据和文件的模型数据,一个消费这线程同时获取两个生产者仓库链表的首节点数据,进行业务处理,周末在家无事编写demo如下:

package produceAndConsume;import java.util.Iterator;import java.util.LinkedList;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/*** 仓库类*/public class MyStorage {    static public int COUNT_NUM = 0;   //消费计数器    static public final int TOTAL_NUM = 20; // 要处理的产品总数    private final int MAX_SIZE = 5;    private final Lock lock = new ReentrantLock();    private final Condition full = lock.newCondition();    private final Condition empty = lock.newCondition();    private LinkedList<String> cimeList = new LinkedList<String>();    private LinkedList<String> sqlList = new LinkedList<String>();public void produceSql(int i) {    lock.lock();    while (sqlList.size() == MAX_SIZE) {        try {            full.await();        } catch (InterruptedException e) {            e.printStackTrace();        }    }    sqlList.add("SQL--" + i);    empty.signal();    lock.unlock();}public void produceCime(int i) {    lock.lock();    while (cimeList.size() == MAX_SIZE) {        try {            full.await();        } catch (InterruptedException e) {            e.printStackTrace();        }    }    cimeList.add("CIME--" + i);    empty.signal();    lock.unlock();}public void consume() {    lock.lock();    while (cimeList.isEmpty() || sqlList.isEmpty())// 没有产品    {        try {            System.out.println("####没有产品了我需要等待生产厂家一会儿####");            empty.await();        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }    Iterator iterCime = cimeList.iterator();    Iterator iterSql = sqlList.iterator();    while (iterCime.hasNext() && iterSql.hasNext())    {        System.out.println(iterCime.next());        System.out.println(iterSql.next());        iterCime.remove();        iterSql.remove();        COUNT_NUM++;    }    full.signalAll();    lock.unlock();}public LinkedList<String> getList() {    return cimeList;}public void setList(LinkedList<String> list) {    this.cimeList = list;}}package produceAndConsume;import java.util.LinkedList;/*** 消费者类*/public class ConsumerTask implements Runnable{private MyStorage storage;private LinkedList<String> list;public ConsumerTask(MyStorage storage) {    this.storage = storage;}@Overridepublic void run() {    while(true)    {        storage.consume();        if (MyStorage.COUNT_NUM == MyStorage.TOTAL_NUM/2)        {            System.out.println("全部处理完成!");            break;        }    }}public MyStorage getStorage() {    return storage;}public void setStorage(MyStorage storage) {    this.storage = storage;}}package produceAndConsume;import java.util.LinkedList;/*** 生产者类*/public class ProduceCimeTask implements Runnable{public ProduceCimeTask(MyStorage storage) {    this.storage = storage;}private MyStorage storage;private LinkedList<String> list;@Overridepublic void run() {    for (int i = 0; i < 20; i+=2)    {        storage.produceCime(i);    }}public MyStorage getStorage() {    return storage;}public void setStorage(MyStorage storage) {    this.storage = storage;}}

测试类与最初的例子类似就不贴了。

第一次写博客可能存在好多不足的地方,或许会有好多不准确的内容希望各位看官指正修改大家互相学习,同时也希望自己能够坚持下去。

原创粉丝点击