因由

说起线程池,大家可能受连接池的印象影响,天然的认为,它应该是一开始有core条线程,忙不过来了就扩展到max条线程,闲的时候又回落到core条线程,如果还有更高的高峰,就放进一个缓冲队列里缓冲一下。

有些整天只和SSH打交道的同学,可能现在还是这样认为的。

无情的现实就是,JDK只有两种典型的线程池,FixedPool 与 CachedPool:

  • FixedPool固定线程数,忙不过来的全放到无限长的缓冲队列里。
  • CachedPool,忙不过来时无限的增加临时线程,闲时回落,没有缓冲队列。

Java ThreadPool的正确打开方式里,建议了如何设置,避免上面两句吓人的“无限”。但无论怎么配,都无法用现成的东西,配出文章一开始的想象图来。

但不得不说,这幅想象图还是比较美好的,特别对于偶有卡顿,偶有不可预测高峰的业务线程池来说。

当然,也有人说请求积压在最后的缓冲队列里不好控制,看具体业务场景了,缓冲队列也有不同的玩法(后详)。

原理

我们的同事老王,研究了一番ThreadPool的机制后,提出了自己实现队列的方式。碰巧,Tomcat也正是这么做的,比起Jetty完全自己写线程池,Tomcat基于JDK的线程池稍作定制,要斯文一些。

JDK线程池的逻辑很简单( 更详细描述还是见Java ThreadPool的正确打开方式 )

- 前core个请求,来一个请求就创建一个线程。
- 之后,把请求插入缓冲队列里让所有的线程去抢;如果插入失败则创建新线程。
- 如果达到max条线程了,抛出拒绝异常。

貌似控制的枢纽都在第2句那里--队列插入的结果。JDK也是通过使用LinkedBlockingQueue 与 特殊的SynchronousQueue,实现自己的控制效果。

那我可不可以自己封装一个Queue,在插入时增加以下逻辑呢?

  • 如果当前有空闲线程等待接客,则把任务加入队列让孩儿们去抢。
  • 如果没有空闲的了,总线程数又没到达max,那就返回false,让Executor去创建线程。
  • 如果总线程数已达到max,则继续把任务加入队列缓冲一下。
  • 如果缓冲队列也满了,抛出拒绝异常。

说白了就是这么简单。

Tomcat的实现

Tomcat的TaskQueue实现:

public class TaskQueue extends LinkedBlockingQueue {

@Override

public boolean offer(Runnable o) {

if (parent . getPoolSize() == parent . getMaximumPoolSize()) return super . offer(o);

if (parent . getSubmittedCount() < parent.getPoolSize()) return super . offer(o);

if (parent . getPoolSize() < parent . getMaximumPoolSize()) return false;

return super.offer(o);

}

}

非常简单的代码,唯一要说明的是,如何判断当前有没有空闲的线程等待接客。JDK的CachedPool是靠特殊的零长度的SynchronousQueue实现。而Tomcat则靠扩展Executor ,增加一个当前请求数的计数器,在execute()方法前加1,再重载afterExecute()方法减1,然后判断当前线程总数是否大于当前请求总数就知道有咩有围观群众。

更进一步

因为相信Tomcat这种百年老店,我们就不自己写这个池了,把Tomcat实现里一些无关需求剥掉即用。

但Tomcat就完美了吗?

首先,TaskQueue的offer()里,调用了executor.getPoolSize(),这是个有锁的函数,这是最遗憾的地方,在大家都在嫌线程池里一条队列锁得太厉害,像ForkJoinPool或Netty的设计都是一个线程一个队列时,这个有锁的函数相当碍眼。而且,最过分的是,Tomcat居然一口气调了三次(在Tomcat9 M9依然如此)。反复看了下,不求那么精准的话貌似一次就够了,真的有并发的变化的情况,executor里还有个处理RejectException,把任务重新放回队列的保险。

最后,说说缓冲队列的两种玩法:

一种是队列相对比较长,比如4096,主线程把任务丢进去就立刻返回了,如果队列满了就直接报拒绝异常。

一种是队列相对比较短的,比如512,如果满了,主线程就以queue.force(command, timeout)等在那里等队列有空,等到超时才报拒绝异常。

Tomcat的机制支持这两种玩法,自己设置就好。

文章可能还要修改,转载请保留原文链接,否则视为侵权:
http://calvin1978.blogcn.com/articles/tomcat-threadpool.html

Tomcat线程池,更符合大家想象的可扩展线程池的更多相关文章

  1. 原生线程池这么强大,Tomcat 为何还需扩展线程池?

    前言 Tomcat/Jetty 是目前比较流行的 Web 容器,两者接受请求之后都会转交给线程池处理,这样可以有效提高处理的能力与并发度.JDK 提高完整线程池实现,但是 Tomcat/Jetty 都 ...

  2. Java:多线程,线程池,用Executors静态工厂生成常用线程池

    一: newSingleThreadExecutor 创建一个单线程的线程池,以无界队列方式运行.这个线程池只有一个线程在工作(如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它.)此线程池 ...

  3. Java线程状态、线程start方法源码、多线程、Java线程池、如何停止一个线程

    下面将依次介绍: 1. 线程状态.Java线程状态和线程池状态 2. start方法源码 3. 什么是线程池? 4. 线程池的工作原理和使用线程池的好处 5. ThreadPoolExecutor中的 ...

  4. 详解线程池的作用及Java中如何使用线程池

    服务端应用程序(如数据库和 Web 服务器)需要处理来自客户端的高并发.耗时较短的请求任务,所以频繁的创建处理这些请求的所需要的线程就是一个非常消耗资源的操作.常规的方法是针对一个新的请求创建一个新线 ...

  5. 零基础学习java------day18------properties集合,多线程(线程和进程,多线程的实现,线程中的方法,线程的声明周期,线程安全问题,wait/notify.notifyAll,死锁,线程池),

    1.Properties集合 1.1 概述: Properties类表示了一个持久的属性集.Properties可保存在流中或从流中加载.属性列表中每个键及其对应值都是一个字符串 一个属性列表可包含另 ...

  6. java并发:线程池、饱和策略、定制、扩展

    一.序言 当我们需要使用线程的时候,我们可以新建一个线程,然后显式调用线程的start()方法,这样实现起来非常简便,但在某些场景下存在缺陷:如果需要同时执行多个任务(即并发的线程数量很多),频繁地创 ...

  7. SEO之HTML优化:让你的网站HTML代码更符合SEO规范

    摘要HTML优化是网站内部优化的重点,可能对SEO新手来说,容易忽略.符合搜索引擎习惯的HTML代码是极利于SEO的,可以让你的网站获得更好的搜索引擎排名.如何制作一个标准的HTML网页,如何做HTM ...

  8. 第11章 Windows线程池(1)_传统的Windows线程池

    第11章 Windows线程池 11.1 传统的Windows线程池及API (1)线程池中的几种底层线程 ①可变数量的长任务线程:WT_EXECUTELONGFUNCTION ②Timer线程:调用 ...

  9. 更符合面向对象的数据库操作方式-OrmLite

    1. jar包下载 下载地址:http://ormlite.com/releases/,一般用core和android包即可. 如果使用的是android studio,也可以直接通过module s ...

随机推荐

  1. <<c 和指针 >> 部分笔记。

    最近竟然对指针有些迷惑了,分不清指针的指向.废话少说,复习.(下面内容来自<<c和指针>>) =指针 ==内存和地址 尽管一个字包含了4个字节,它仍然只有一个地址.至于是最左边 ...

  2. cocos2dx开发笔记

    1.帧动画:SpriteTest=>SpriteAnimationSplit 2.sourceinsight显示代码行 option->document option->editin ...

  3. StaggeredGridLayoutManager

    Class Overview A LayoutManager that lays out children in a staggered grid formation. It supports hor ...

  4. C#画图解决闪烁问题

    导致画面闪烁的关键原因分析:       一.绘制窗口由于大小位置状态改变进行重绘操作时,绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面重新刷新一次以维持窗口正常显示 ...

  5. C#中默认的修饰符

    参考自Default visibility for C# classes and members (fields, methods, etc)? Classes and structs that ar ...

  6. C结构体之位域(位段)

    C结构体之位域(位段) 有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位.例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可.为了节省存储空间,并使处理简便,C ...

  7. 使用截图方式将Excel导出为PNG图片的不可行性

    博主前面一篇文章使用了JAVA的Robot机制 模拟打开Excel然后Robot移动到指定区域,截图并生成PNG格式图片 试图使用这种方式将复杂的Excel报表转化成无差别的PNG图片 但是这种方式遇 ...

  8. uva10820Send a Table

    筛法. 首先使cnt[i]=sqr(n/i),这样cnt[i]就表示gcd(x,y)大于等于i的数对的个数,然后倒序枚举减去gcd大于i的个数就可以得到ans[i].最终得到ans[1]. 这个算法单 ...

  9. WEB前端开发成长指南

    小 编注:相比起网页射击狮,操纵代码的前端攻城狮凭着双手在键盘巴拉巴拉敲出的字符,就能赋予二次元的静态页面生命,各种lovely 的~~fabulous的~~elegant的交互效果,那叫一个锦上添花 ...

  10. php复制目录及文件

    <?php /* 复制目录 */ function copydir($dirsrc,$dirto){ if(is_file($dirto)){ echo "目标不是目录不能创建&quo ...