多线程设计模式——Producer-Consumer生产者消费者模式

原创 2016年07月08日 21:59:49

这些都是根据我最近看的《Java实战指南多线程编程(设计模式篇)》所得整理。

模式名称

Producer-Consumer生产者消费者模式

模式面对的问题

有的线程的结果是另外一些线程的原料,也就是说,一些线程是生产者,另外一些线程是消费者,消费者需要生产者生产的东西才能正常运行,协调两者的关系成了一个大的问题。

解决思路

有一个中间的存储位置,用来存储生产者生产出来的东西,称之为通道。
Producer生产者
Product生产者所生产的任务
Channel通道的抽象
BlockingQueueChannel基于阻塞队列的Channel实现
Consumer消费者

Created with Raphaël 2.1.0ClientClientProducterProducterproductproductChannelChannel1service()2creat3put()45

例子代码

某内容管理系统需要支持对文档附件中的文件进行全文检索。改系统中,附件会被上传到专用的文件服务器上,对附件进行全文检索的功能模块也是部署在文件服务器上的。
模式主类

public class AttachmentProcessor {
    private final String ATTACHMENT_STORE_BASE_DIR = "/home/viscent/tmp/attachments/";

    //模式角色Producer-Consumer.Channer
    priuvate final Channer<File> channer = new BlockingQueueChannel<File>(
            new ArrayBlockingQueue<File>(200));
    //模式角色Producer-Consumer.Consumer
    private final AbstractTerminatableThread indexingThread = new
        AbstractTerminatableThread(){
        @Override
        protected void doRun()throws Exception{
            File file =null;

            file = channel.take();
            try{
                indexFile(file);
            }catch(Exception e){
                e.printStackTrace();
            }finally{
                terminationToken.reservations.decrementAndGet();
            }
        }

        //根据制定文件生成全文搜索所需的索引文件
        private void indexFile(File file) throws Exception{
            //省略与模式无关的代码

            //模拟生成索引文件的时间消耗
            Radom rnd =new Random();
            try{
                Thread.sleep(rnd.nextInt(100));
            }catch(InterruptedException e){
                ;
            }
        }
    };

    public void init(){
        indexingThread.start();
    }

    public void shutdown(){
        indexingThread.terminate();
    }

    public void saveAttachment(InputStream in,
            String documentId,String originalFileName)throws IOException{
        File file = saveAsFile(in,documentId,originalFileName);
        try{
            channel.put(file);
        }catch(InterruptedException e){
            ;
        }
        indexingTread.terminationToken.reservations.incrementAndGet();
    }

    private FTPClient initFTPClient(String ftpServer,String userName,
            String password) throws Exception{
        FTPClient ftpClient = new FTPClient();

        FTOClientConfig config = new FTPClientConfig();
        ftpClient.config(config);

        int reply;
        ftpClient.connect(ftpServer);

        System.out.print(ftpClient.getReplyString());

        reply = ftpClient.getReplyCode();

        if(!dirName.equals(file.getCanoicalFile().getParent())){
            throw new SecurityException("Invalid originalFileName:"+originalFileName);
        }

        BufferedOutputStream bos =null;
        BufferedInputStream bis = new BufferedInputStream(in);
        byte[]buf = new byte[2048];
        int len = -1;
        try{
            bos =new BufferedOutputStream(new FileOutputSteram(file));
            while((len = bis.read(buf) > 0)){
                bos.write(buf,0,len);
            }
            bos.flush();
        }finally{
            try{
                bis.close();
            }catch(IOException e){
                ;
            }
            try{
                if(null != bos){
                    bos.close();
                }
            }catch(IOException e){
                ;
            }
        }

        ftpClient.setFileType(FTP.ASCII_FILE_TYPE);
        return ftpClient;
    }
}

Channel接口

public interface Channel<P> {
    //从通道中取一个"产品"。
    P take() throws InterruptedException;

    //往通道里面存储一个"产品"。
    void put(P product) throws InterruptedException;
}

BlockingQueueChannel类

public class BlockingQueueChannel<P> implements Channel<P> {
    private final BlockingQueue<P>queue;
    public BlockingQueueChannel(BlockingQueue<P>queue){
        this.queue = queue;
    }

    @Override
    public P take() throws InterruptedException{
        return queue.take();
    }


    @Override
    public void put(P product) throws InterruptedException{
        queue.put(product);
    }
}

模式的评价与实现考量

生产这消费者模式是一个经典的线程模式吗,但是它也有一些容易出现的问题:
1. 管道积压:生产者消费者模式中消费者的处理能力往往低于生产这的处理能力,会出现管道挤压的现象。处理这种现象,有集中方法:使用有界阻塞队列,队列到一定数量就不在生产,等待消费;使用有流量控制的无界阻塞队列,在线程的时间分配时对生产者的时间进行限制来平衡。
2. 工作窃取算法:如果是多个消费者从管道中取得产品,会出现线程安全的问题,所以会有一个通道实例对应多个队列实例来处理。
3. 线程的停止:整个模式也可以看做一个线程,这个线程的停止会比一般的线程要复杂一些,需要注意处理。
4. 高性能高可靠性:这里的示例代码是一个比较一般的实现,如果有较高的要求,可以考虑Producer-Consumer模式实现库LMAX Disruptor:https://github.com/LMAX-Exchange/disruptor

版权声明:本文为博主原创文章,未经博主允许不得转载。

java 多线程并发系列之 生产者消费者模式的两种实现

生产者消费者模式是并发、多线程编程中经典的设计模式,生产者和消费者通过分离的执行工作解耦,简化了开发模式,生产者和消费者可以以不同的速度生产和消费数据。 真实世界中的生产者消费者模式...
  • yujin753
  • yujin753
  • 2015年05月14日 16:59
  • 11606

并发设计模式之生产者-消费者模式

点击查看原文: http://www.joyhwong.com/2016/11/19/并发设计模式之生产者-消费者模式/ 生产者-消费者模式是一个经典的多线程设计模式,它为多线程间的协作提供了良...
  • liuchuo
  • liuchuo
  • 2016年11月25日 14:10
  • 1075

JAVA多线程(三)生产者消费者模式及实现方法

介绍了生产者消费者模式以及实现方法(wait¬ify,阻塞队列
  • antony9118
  • antony9118
  • 2016年05月23日 15:15
  • 2142

Qt之线程同步(生产者消费者模式 - QSemaphore)

简述生产者将数据写入缓冲区,直到它到达缓冲区的末尾,这时,它从开始位置重新启动,覆盖现有数据。消费者线程读取数据并将其写入标准错误。Semaphores 使得比 mutexes 有一个更高级的并发性成...
  • u011012932
  • u011012932
  • 2016年09月23日 16:33
  • 4127

生产者消费者模式的几种实现方式以及线程安全问题

生产者消费者模式的几种实现方式拿我们生活中的例子来说,工厂生产出来的产品总是要输出到外面使用的,这就是生产与消费的概念。 在我们实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数...
  • noaman_wgs
  • noaman_wgs
  • 2017年03月27日 11:40
  • 1135

C++多线程学习:生产者消费者问题

多线程相关知识点: C++11 线程库:http://zh.cppreference.com/w/cpp/thread 互斥量和锁 std::unique_lock::lock 和 std::uni...
  • quzhongxin
  • quzhongxin
  • 2015年08月19日 20:56
  • 5542

线程_死锁_生产者消费者模式(信号灯法)_任务调度JAVA180-182

一、S02E180_01线程_死锁二、S02E181_01线程_生产者消费者模式(信号灯法)三、S02E182_01线程_任务调度...
  • u013292493
  • u013292493
  • 2016年01月24日 22:47
  • 383

Android平台多线程实现生产者-消费者模型

本示例利用线程容器-ThreadPoolExecutor 运行消费者任务线程,基于公平锁机制,控制消费者线程的中断(公平锁相对非公平锁在性能上会有所牺牲,但在执行诸如下载大文件这样的耗时任务时,能体现...
  • zhangbuzhangbu
  • zhangbuzhangbu
  • 2014年05月14日 10:53
  • 1286

Java 多线程设计模式之Producer-Consumer

Producer-Consumer 模式通过在数据的生产者和消费者之间引入一个通道(Channel, 暂时可以将其简单地理解为一个队列)对二者进行解耦(Decouping):生产者将其生产的数据放入通...
  • lilele12211104
  • lilele12211104
  • 2017年12月08日 11:10
  • 31

生产者/消费者模式(阻塞队列)

生产者/消费者模式(阻塞队列) 博客分类:  j2EE 制造thread多线程软件测试JDK  生产消费者模式  貌似也是阻塞的问题  花了一些时间终于弄明白这个鸟...
  • autumn20080101
  • autumn20080101
  • 2013年07月26日 08:48
  • 37868
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:多线程设计模式——Producer-Consumer生产者消费者模式
举报原因:
原因补充:

(最多只允许输入30个字)