并发编程之生产者消费者

生产者和消费者模式

      生产者和消费者模式通过平衡生产和消费线程的工作能力来提高程序整体处理数据的速度。
      在线程世界里,生产者 ——生产数据的线程,消费者——消费数据的线程。在多线程开发中如果生产者和消费者处理速度不一致,必然会导致一方处于等待状态,为了解决生产消费不均衡的状态,便有了生产者和消费者模式。

什么是生产者和消费者模式

     生产者和消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。它们之间不直接通信,而是通过阻塞队列通信,所以生产者生产完数据后,不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队里相当于一个缓冲区,平衡彼此之间的处理能力。
     这个阻塞队里就是用来给生产者和消费者解耦的。我们可以看到大多的设计模式中都会找一个第三者来解耦,如工厂模式的第三者是工厂类,模板模式的第三者是模板类等。Spring中的IOC之类大概也是用到了此类思想。

实例

某邮件系统用来分享文章,在1.0版本中使用单线程,在一个线程里程序先抽取全部邮件,转化为文章对象 ,然后添加全部的文章,最后删除抽取过的邮件。

public static extract() {

  logger.debug("begin" + getExtractorName() + "..");
   // acquire email
   List
   
   
articles = extractEmail(); //add Article for(Article article :Articles) { addArticleOrComment(article); } // clean email cleanEmail(); logger.debug("end " + getExtractorName() + ".." ) }
  当越来越多的用户使用这个系统师,处理的速度越来越慢,于是在2.0版本中使用了生产者和消费者模式来处理邮件,首先生产者线程按一定的规则去邮件系统抽取邮件,然后存放在阻塞队列里,消费者从阻塞队列里抽取文章后插入到conflunce里。代码如下


public class QuickEmialToWikiExtractor extends AbstExtractExtractor {

    private ThreadPoolExecutor threadsPool;
    private ArticleBlockingQueue
    
    
     
      emailQueue;

    public QuickEmialToWikiExtractor() {
        emailQueue = new ArticleBlocExchangeEmailShallowDTOkingQueue
     
     
      
      ();
        int corePoolSize = Runtime.getRuntime().availableProcessors() * 2;
        threadsPool = new ThreadPoolExecutor(corePoolSize, corePoolSize, 101, TimeUnit.SECONDS,
                new LinkedBlockingQueue
      
      
       
       (2000));
    }

    @Override
    public void extract() {
        logger.debug("begin" + getExtractorName() + "..");
        long start = System.currentTimeMillis();
        // 抽取所有邮件放到队列里
        new ExtractEmailTask().start();
        // 把队列里的文章插入到Wiki中
        insertToWiki();
        long end = System.currentTimeMillis();
        double cost = (end - start) / 1000;
        logger.debug("end" + getExtractorName() + "spend time:" + cost + "..");
    }

    /*
     * (non-Javadoc)
     *
     * @see test.AbstExtractExtractor#insertToWiki() 把队列里的文章插入到Wiki中
     */
    @Override
    protected void insertToWiki() {
        // login Wiki, 每隔一段时间就需要登录一次
        confluenceService.login(RuleFactory.USER_NAME, RuleFactory.PASSWORD);

        while (true) {
            ExchangeEmailShallowDTO email = emailQueue.poll(2, TimeUnit.SECONDS);
            if (email == null) {
                break;
            }
            threadsPool.submit(new insertToWikiTask(email));
        }

    }

    protected List
      
      
     
     
    
    
   
   
extractEmail() { List allEmails = getEmailService().queryAllEmails(); if (allEmails == null) { return null; } for (ExchangeEmailShallowDTO exchangeEmailShallowDTO : allEmails) { exchangeEmailShallowDTO.offer(exchangeEmailShallowDTO); } return null; } /* * 抽取邮件任务 * */ public class ExtractEmailTask extends Thread { @Override public void run() { extractEmail(); } } }
执行逻辑是,生产者启动一个线程把所有的邮全部抽取到队列中,消费者启动CPU*2个线程数处理邮件,从之前的单线程处理到现在的多线程处理,并且抽取邮件的线程不需要等待处理邮件的线程运行完再抽取新的邮件,所以使用生产者和消费者模式比以前要快了几倍。

参考书籍《Java并发编程的艺术》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值