生产者和消费者模式
生产者和消费者模式通过平衡生产和消费线程的工作能力来提高程序整体处理数据的速度。
在线程世界里,生产者 ——生产数据的线程,消费者——消费数据的线程。在多线程开发中如果生产者和消费者处理速度不一致,必然会导致一方处于等待状态,为了解决生产消费不均衡的状态,便有了生产者和消费者模式。
在线程世界里,生产者 ——生产数据的线程,消费者——消费数据的线程。在多线程开发中如果生产者和消费者处理速度不一致,必然会导致一方处于等待状态,为了解决生产消费不均衡的状态,便有了生产者和消费者模式。
什么是生产者和消费者模式
生产者和消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。它们之间不直接通信,而是通过阻塞队列通信,所以生产者生产完数据后,不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队里相当于一个缓冲区,平衡彼此之间的处理能力。
这个阻塞队里就是用来给生产者和消费者解耦的。我们可以看到大多的设计模式中都会找一个第三者来解耦,如工厂模式的第三者是工厂类,模板模式的第三者是模板类等。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(); } } }
参考书籍《Java并发编程的艺术》