当前位置:  编程技术>java/j2ee

支持生产阻塞的Java线程池

    来源: 互联网  发布时间:2014-11-05

    本文导语:  通常来说,生产任务的速度要大于消费的速度。一个细节问题是,队列长度,以及如何匹配生产和消费的速度。 一个典型的生产者-消费者模型如下:   在并发环境下利用J.U.C提供的Queue实现可以很方便地保证生产和消费...

通常来说,生产任务的速度要大于消费的速度。一个细节问题是,队列长度,以及如何匹配生产和消费的速度。

一个典型的生产者-消费者模型如下:

 
在并发环境下利用J.U.C提供的Queue实现可以很方便地保证生产和消费过程中的线程安全。这里需要注意的是,Queue必须设置初始容量,防止生产者生产过快导致队列长度暴涨,最终触发OutOfMemory。

对于一般的生产快于消费的情况。当队列已满时,我们并不希望有任何任务被忽略或得不到执行,此时生产者可以等待片刻再提交任务,更好的做法是,把生产者阻塞在提交任务的方法上,待队列未满时继续提交任务,这样就没有浪费的空转时间了。阻塞这一点也很容易,BlockingQueue就是为此打造的,ArrayBlockingQueue和LinkedBlockingQueue在构造时都可以提供容量做限制,其中LinkedBlockingQueue是在实际操作队列时在每次拿到锁以后判断容量。

更进一步,当队列为空时,消费者拿不到任务,可以等一会儿再拿,更好的做法是,用BlockingQueue的take方法,阻塞等待,当有任务时便可以立即获得执行,建议调用take的带超时参数的重载方法,超时后线程退出。这样当生产者事实上已经停止生产时,不至于让消费者无限等待。

于是一个高效的支持阻塞的生产消费模型就实现了。

等一下,既然J.U.C已经帮我们实现了线程池,为什么还要采用这一套东西?直接用ExecutorService不是更方便?

我们来看一下ThreadPoolExecutor的基本结构:

 
可以看到,在ThreadPoolExecutor中,BlockingQueue和Consumer部分已经帮我们实现好了,并且直接采用线程池的实现还有很多优势,例如线程数的动态调整等。

但问题在于,即便你在构造ThreadPoolExecutor时手动指定了一个BlockingQueue作为队列实现,事实上当队列满时,execute方法并不会阻塞,原因在于ThreadPoolExecutor调用的是BlockingQueue非阻塞的offer方法:

代码如下:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
        if (runState == RUNNING && workQueue.offer(command)) {
            if (runState != RUNNING || poolSize == 0)
                ensureQueuedTaskHandled(command);
        }
        else if (!addIfUnderMaximumPoolSize(command))
            reject(command); // is shutdown or saturated
    }
}

这时候就需要做一些事情来达成一个结果:当生产者提交任务,而队列已满时,能够让生产者阻塞住,等待任务被消费。

关键在于,在并发环境下,队列满不能由生产者去判断,不能调用ThreadPoolExecutor.getQueue().size()来判断队列是否满。

线程池的实现中,当队列满时会调用构造时传入的RejectedExecutionHandler去拒绝任务的处理。默认的实现是AbortPolicy,直接抛出一个RejectedExecutionException。

几种拒绝策略在这里就不赘述了,这里和我们的需求比较接近的是CallerRunsPolicy,这种策略会在队列满时,让提交任务的线程去执行任务,相当于让生产者临时去干了消费者干的活儿,这样生产者虽然没有被阻塞,但提交任务也会被暂停。

代码如下:

public static class CallerRunsPolicy implements RejectedExecutionHandler {
    /**
     * Creates a CallerRunsPolicy.
     */
    public CallerRunsPolicy() { }

    /**
     * Executes task r in the caller's thread, unless the executor
     * has been shut down, in which case the task is discarded.
     * @param r the runnable task requested to be executed
     * @param e the executor attempting to execute this task
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}

但这种策略也有隐患,当生产者较少时,生产者消费任务的时间里,消费者可能已经把任务都消费完了,队列处于空状态,当生产者执行完任务后才能再继续生产任务,这个过程中可能导致消费者线程的饥饿。

参考类似的思路,最简单的做法,我们可以直接定义一个RejectedExecutionHandler,当队列满时改为调用BlockingQueue.put来实现生产者的阻塞:

代码如下:

new RejectedExecutionHandler() {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                if (!executor.isShutdown()) {
                        try {
                                executor.getQueue().put(r);
                        } catch (InterruptedException e) {
                                // should not be interrupted
                        }
                }
        }
};

这样,我们就无需再关心Queue和Consumer的逻辑,只要把精力集中在生产者和消费者线程的实现逻辑上,只管往线程池提交任务就行了。

相比最初的设计,这种方式的代码量能减少不少,而且能避免并发环境的很多问题。当然,你也可以采用另外的手段,例如在提交时采用信号量做入口限制等,但是如果仅仅是要让生产者阻塞,那就显得复杂了。


    
 
 

您可能感兴趣的文章:

  • aix 不支持非阻塞么?
  • 请问Linux核心支持多线程吗?开发库有线程库吗?线程好用吗?(稳定?)
  • linux内核是如何支持多线程的?
  • aix支持POSIX线程标准吗?
  • fedora10下支持线程的并发设置吗?
  • 请问谁在SCO上做过开发?支持多线程开发吗?
  • 如何编写支持超线程的Linux程序
  • 请问:linux下面支持多线程编程吗?
  • aix支持线程吗
  • 谁有多线程、支持断点续传的javabean?
  • win32系统最多支持多少线程?
  • 支持多线程(多进程)的子程序(模块)应该具备哪些特点?
  • QT3.1.2安装后 如何配置才能支持多线程
  • P2 350的cpu,最大限度支持多少线程?
  • Java虚拟机最多支持多少个线程的探讨
  • 命令行使用支持断点续传的java多线程下载器
  • python支持断点续传的多线程下载示例
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • Docker支持的安装方式
  • php for linux安装既不支持mysql.如何配置支持mysql
  • Ubuntu 12.04长期支持版和最新版Ubuntu 13.10下载
  • 在red hat 9.0.1支持u盘吗,支持如何驱动呀?谢了先。
  • Docker宣布支持Windows 10和Azure Windows Server
  • 到底有哪些网站支持JSP???163.com的免费空间支持吗?
  • windows10玩游戏怎么样?唯一支持DirectX 12的windows
  • 急急急,2.4.20的内核不支持pci-e,请问如何才能使其支持?
  • Docker支持更深入的容器日志分析
  • JBuilder4 能支持 Weblogic 6.0吗?怎麽支持?
  • ​Windows Server 2016提供Docker原生运行的企业级支持
  • RedHat 7.1英文版怎么样通过外挂支持中文?只要求支持......
  • nginx服务器下通过fastcgi支持php5详细安装配置步骤
  • 请问 solaris支持不支持以下几种格式的读取啊
  • HTML <strike> 标签 - HTML5 不支持
  • IE6对XML的支持比较好,但不支持JAVA……
  • HTML <tt> 标签 - HTML5 不支持
  • win7 下不支持andliunx 虚拟机,有别的支持win7的吗
  • HTML <font> 标签 - HTML5 不支持
  • 请问mozilla(redhat9里的)对javascript的支持程度,是否支持iframe?参与有分
  • HTML <basefont> 标签 - HTML5 不支持
  • JBuilderIDE中怎么才能支持鼠标的滚动轴啊?这都不支持好别扭~


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3