ThreadPool的问题

为什么要有线程池

因为不让主要的service线程卡主,可以继续serve新来的task。记得刚毕业的时候面试,面试官问如果一个客户端请求服务端80端口,建立连接后服务端的端口是多少,如果这时又有两个请求进来了它们和哪个端口建立连接等等。其实和这个很类似,主线程很快处理完请求,fork主进程或者创建新线程,“异步”地运行这个请求,这样主线程就不会被block,自然端口也是服务器端1024-65535之间的随机端口。

那么问题来了,如果任意来一个新的Task我们都建一个新的Thread然后执行,随后完成,再结束它。这里会有很多开销,包括创建Thread的CPU和内存,执行结束后再通过GC回收这个内存资源。

那么怎么可以提升它的效率呢,这就好像PV操作,就像火车站飞机场,在没有旅客的时候,出租车(consumer)都在等待,乘客(producer)来了,出租车(consumer)从队列里接走一名乘客,等待乘客减一,出租车也减一,以此类推。此处乘客相当于向线程池提交的Task,线程池里的Thread相当于出租车。乘客太多,出租车太少,会造成乘客排队过长,最终导致车站崩溃。如果乘客太少,出租车太多,会造成资源浪费,出租车接不到人,干等,也赚不到钱,其他地方需要坐出租车的人等不到车。那么黄金配比是多少呢?

线程数 = CPU核数 * (1 + 线程等待时间 / 线程执行时间)

出租车和乘客的模型只可以类比,不可以完全套用在线程池问题上,因为车站里可没有CPU核数这个东西,如果算作可以同时处理Task的个数,那么对于出租车来说,是趋于无线大的,因为公路允许非常多的车同时行驶,就是说只要资源允许,理想情况是,有多少乘客就有多少出租车,因为可以一下全都处理完,但现实是车站的车次和客流量的平均值是一定的,出租车资源也是有限的,那么只要出租车数的平均值可以匹配上这个客流量的平均值即可。这就是不同于线程池的地方。

扯远了,说回线程池,那么现在有了公式,怎么算理想的大小呢。这就得加一些Metric(数值统计,标记)了,比如在执行I/O任务的时候记录下时间,这就是等待时间,再在Task的开头结尾记录下,然后最终减去这个等待时间,剩下的就是CPU真正处理的时间。为什么I/O算作等待时间,因为I/O任务基本都是内核在与网络或磁盘等做交互,等待对方处理完后把数据写入缓冲区,我们本机的CPU是不花时间的,当缓冲区准备完毕会产生一个interrupt(中断)通知CPU,此时CPU把这个Task的状态从waiting改为ready,然后继续参与时间片轮转。所以其实I/O也是会用一小部分CPU的时间的,但考虑到相比于等待I/O和写入缓冲区并产生interrupt,这些CPU时间基本可以忽略不计。最终我们有了waiting time和CPU time,就可以按照核数大致算出一个合理的线程池大小,记住这里的大小只是理论上的,最终还应该结合Stress Test以及真实产品的情况再做调整。

线程池的参数

参数请参考之前我写过的一个文章,ThreadPoolExecutor

Thread Pool基本上有这么几个比较重要的参数,coreSizemaxSizeblockingQueuerejectedExceptionHandler

  • coreSize就是线程池要维护的核心线程数,大于这个的话,如果超过了keepAlive时间就会被干掉,最终保持coreSize
  • maxSize就是最大的线程数,超过这个数量就会被拒绝,rejectedExceptionHandler就会收到通知并处理
  • blockingQueue,阻塞队列,大致有四种,常用三种,BlockingArrayList,BlockingLinkedList和SynchronousQueue还有DelayedWorkerQueue。BlockingArrayList和BlockingLinkedList就是数组和链表,这个数组初始化的时候必须指定大小,链表也可以设置大小,这个大小就是blocking queue size后面会讲到。SynchronousQueue就是offer的同时必须有poll,所以可以简单理解为它是一个size为0的queue,offer的时候会返回false。Delayed queue因为没有用到,不在这里展开。
  • rejectedExceptionHandler,如果超过了maxSize会抛出异常,这个handler就是来处理异常的,大致有这么几种handler,AbortPolicy,默认的策略,直接抛出RuntimeException,DiscardPolicy,安静的丢弃,CallerRunPolicy,如果线程池没有关闭则主线程来执行任务,DiscardOldestPolicy和DiscardPolicy类似,不过是从queue里取出最老的丢弃,然后再重新提交。

分配策略

如果一个任务提交进来:

  1. 先判断当前的poolSize是否小于coreSize,如果小于则直接创建;
  2. 如果大于等于,则把Task提交到BlockingQueue;
  3. 根据BlockingQueue的特性进行排队,如果排队失败或者队列已满,则尝试创建新线程;
  4. 如果总线程数小于maxSize则创建成功并开始运行,如果大于等于则抛出异常;
  5. RejectExceptionHandler接收到异常进行下一步处理;

遇到的问题

产品里遇到的问题是用于日志记录和展示方法调用图的一个线程池满了,导致无法再创建新的线程,最终拒绝任务,让整个请求失败。是一个P100的Metric,P99.9都没有问题,所以本地很难复现,只可以通过修改线程池大小为1同时增大任务处理时间尝试复现。
这里就先不贴图了,当线程池满了后,默认采用了AbortPolicy然后让整个请求失败了。我们是Tier-1的Service,对于这种用于debug的线程池我们是不允许fail掉整个请求的。解决办法也很简单,就是采用DiscardPolicy,同时override里面的handler方法,添加自己的代码对异常进行记录,这样我们就对何时失败以及单位时间内抛出了多少异常了如指掌了,这也很帮助我们调整合理的线程池大小。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容