Java ThreadPool的正确打开方式花钱的年华 | 江南白衣(5星推荐)
线程池应对于突然增大、来不及处理的请求,无非两种应对方式:
- 将未完成的请求放在队列里等待
- 临时增加处理线程,等高峰回落后再结束临时线程
JDK的Executors.newFixedPool() 和newCachedPool(),分别使用了这两种方式。
不过,这俩函数在方便之余,也屏蔽了ThreadPool原本多样的配置,对一些不求甚解的码农来说,就错过了一些更适合自己项目的选择。
1. ThreadPoolExecutor的原理
经典书《Java Concurrency in Pratice(Java并发编程实战)》的第8章,浓缩如下:
1. 每次提交任务时,如果线程数还没达到coreSize就创建新线程并绑定该任务。
所以第coreSize次提交任务后线程总数必达到coreSize,不会重用之前的空闲线程。
在生产环境,为了避免首次调用超时,可以调用executor.prestartCoreThread()预创建所有core线程,避免来一个创一个带来首次调用慢的问题。
2. 线程数达到coreSize后,新增的任务就放到工作队列里,而线程池里的线程则努力的使用take()阻塞地从工作队列里拉活来干。
3. 如果队列是个有界队列,又如果线程池里的线程不能及时将任务取走,工作队列可能会满掉,插入任务就会失败,此时线程池就会紧急的再创建新的临时线程来补救。
4. 临时线程使用poll(keepAliveTime,timeUnit)来从工作队列拉活,如果时候到了仍然两手空空没拉到活,表明它太闲了,就会被解雇掉。
5. 如果core线程数+临时线程数 >maxSize,则不能再创建新的临时线程了,转头执行RejectExecutionHanlder。默认的AbortPolicy抛RejectedExecutionException异常,其他选择包括静默放弃当前任务(Discard),放弃工作队列里最老的任务(DisacardOldest),或由主线程来直接执行(CallerRuns),或你自己发挥想象力写的一个。 
2. FixedPool 与 CachedPool
FixedPool默认用了一条无有暗香盈袖界的工作队列 LinkedBlockingQueue, 所以只去到上面的第2步就不会继续往下走了,coreSize的线程做不完的任务不断堆积到无限长的Queue中。
所以只有coreSize一个参数,其他maxSize,keepAliveTime,RejectHandler的配置都不会实际生效。
CachedPool则把coreSize设成0,然后选用了一种特殊的Queue -- SynchronousQueue,只要当前没有空闲线程,Queue就会立刻报插入失败,让线程池增加新的临时线程,默认的KeepAliveTime是1分钟,而且maxSize是整型的最大值,也就是说只要有干不完的活,都会无限增增加线程数,直到高峰过去线程数才会回落。
3. 对FixedPool的进一步配置
3.1 设置QueueSize
如果不想搞一条无限长的Queue,避免任务无限等待显得像假死,同时占用太多内存,可能会把它换成一条有界的ArrayBlockingQueue,那就要同时关注一下这条队列满了之后的场景,选择正确的rejectHanlder。
此时,最好还是把maxSize设为coreSize一样的值,不把临时线程及其keepAlive时间拉进来,Queue+临时线程两者结合听是好听,但很难设置好。
3.2 有界队列选LinkedBlockingQueue 还是ArrayBlockingQueue?
按Executors的JavaDoc上说是ArrayBlockingQueue,起码ArrayBlockingQueue每插入一个Runnable就直接放到内部的数组里,而LinkedBlockingQueue则要 new Node(runnable),无疑会产生更多对象。而性能方面有兴趣的同学可以自己测一下。
allowCoreThreadTimeOut(true)
允许core线程也在完全没流量时收缩到0,但因为JDK的算法,只要当前线程数低于core,请求一来就会创建线程,不管现在有没有空闲的线程能服务这个请求,所以这个选项的作用有限,仅在完全没流量时有效。 但都完全没流量了,怎么滴其实也没所谓了。除非是同时有很多个线程池的情况。
4. 对CachedPool的进一步配置
4.1 设置coreSize
coreSize默认为0,但很多时候也希望是一个类似FixedPool的固定值,能处理大部分的情况,不要有太多加加减减的波动,等待和消耗的精力。
4.2 设置maxSize及rejectHandler
同理,maxSize默认是整形最大值,但太多的线程也很可能让系统崩溃,所以建议还是设一下maxSize和rejectHandler。
4.3 设置keepAliveTime
默认1分钟,可以根据项目再设置一把。
4.4 SynchronousQueue的性能?
高并发下,SynchronousQueue的性能绝对比LinkedBlockingQueue/ArrayBlockingQueue低一大截。虽然JDK6的实现号称比JDK5的改进很多,但还是慢,据文章说只在20线程并发下它才是快的。
5. SpringSide的ThreadPoolBuilder
广告时间,SpringSide的ThreadPoolBuilder能简化上述的配置。
此文太科普太水,主要就是为了帮SpringSide-Utils项目打广告:)
有关的...
- 2016-10-29 -- Java性能优化指南1.8版,及唯品会的实战
- 2016-10-26 -- 关键业务系统的JVM参数推荐(2016热冬版)
- 2016-09-14 -- Btrace入门到熟练小工完全指南
- 2016-08-27 -- 高性能场景下,Map家族的优化使用建议
Java ThreadPool的正确打开方式花钱的年华 | 江南白衣(5星推荐)的更多相关文章
- C#语法——泛型的多种应用 C#语法——await与async的正确打开方式 C#线程安全使用(五) C#语法——元组类型 好好耕耘 redis和memcached的区别
C#语法——泛型的多种应用 本篇文章主要介绍泛型的应用. 泛型是.NET Framework 2.0 版类库就已经提供的语法,主要用于提高代码的可重用性.类型安全性和效率. 泛型的定义 下面定义了 ...
- List的remove()方法的三种正确打开方式
转: java编程:List的remove()方法的三种正确打开方式! 2018年08月12日 16:26:13 Aries9986 阅读数 2728更多 分类专栏: leetcode刷题 版权声 ...
- iOS开发小技巧--相机相册的正确打开方式
iOS相机相册的正确打开方式- UIImagePickerController 通过指定sourceType来实现打开相册还是相机 UIImagePickerControllerSourceTypeP ...
- Xcode 的正确打开方式——Debugging(转载)
Xcode 的正确打开方式——Debugging 程序员日常开发中有大量时间都会花费在 debug 上,从事 iOS 开发不可避免地需要使用 Xcode.这篇博客就主要介绍了 Xcode 中几种能 ...
- InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式
InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式 https://mp.weixin.qq.com/s/HGa_90XvC22anabiBF8AbQ 在这篇文章里,我将讨论在MySQL 5 ...
- Console控制台的正确打开方式
Console控制台的正确打开方式 console对象提供了访问浏览器调试模式的信息到控制台 -- Console对象 |-- assert() 如果第一个参数断言为false,则在控制台输出错误信息 ...
- 任务队列和异步接口的正确打开方式(.NET Core版本)
任务队列和异步接口的正确打开方式 什么是异步接口? Asynchronous Operations Certain types of operations might require processi ...
- (一)Redis for Windows正确打开方式
目录 (一)Redis for Windows正确打开方式 (二)Redis for 阿里云公网连接 (三)Redis for StackExchange.Redis 下载地址 官网.中文网1 及 中 ...
- C++11随机数的正确打开方式
C++11随机数的正确打开方式 在C++11之前,现有的随机数函数都存在一个问题:在利用循环多次获取随机数时,如果程序运行过快或者使用了多线程等方法,srand((unsigned)time(null ...
随机推荐
- django——inclusion_tag
inclusion_tag() 原型: django.template.Library.inclusion_tag() 主要作用:通过渲染一个模板来显示一些数据. 例如,Django的Admin界面使 ...
- 图的基本操作(基于邻接矩阵):图的构造,深搜(DFS),广搜(BFS)
#include <iostream> #include <stdio.h> #include <cstdlib> #include <cstring> ...
- full GC触发的条件
full GC触发的条件除直接调用System.gc外,触发Full GC执行的情况有如下四种.1. 旧生代空间不足旧生代空间只有在新生代对象转入及创建为大对象.大数组时才会出现不足的现象,当执行Fu ...
- [原创]H5前端性能测试工具介绍
[原创H5前端性能测试工具介绍 一 网络抓包工具 网络抓包工具选择原则,可以捕获网络请求,抓取具体请求信息流,同时可以针对网络请包进行修改或拦截: 1.Fiddler(推荐) 2.Charles(推荐 ...
- 如何在本地搭建一个Android应用crashing跟踪系统-ACRA
https://github.com/bboyfeiyu/android-tech-frontier/tree/master/others/%E5%A6%82%E4%BD%95%E5%9C%A8%E6 ...
- Unity Shader-后处理:Bloom全屏泛光
一.简介 今天来学习一下全屏Bloom效果,有时候也叫Glow效果,中文一般叫做“全屏泛光”,这是一种可以模拟出HDR的全屏后处理效果,但是实现原理与HDR相差很远,效果比HDR差一些,但是比HD ...
- C++ 并发编程,std::unique_lock与std::lock_guard区别示例
背景 平时看代码时,也会使用到std::lock_guard,但是std::unique_lock用的比较少.在看并发编程,这里总结一下.方便后续使用. std::unique_lock也可以提供自动 ...
- Java access to the Domino Objects, Part 1
From: https://www.ibm.com/developerworks/lotus/library/ls-Java_access_pt1/index.html Overview Java a ...
- MYSQL浮点型转int类型
cast('3.15926' as signed) cast(浮点型 as signed)
- testng.xml 配置大全
1.TestNG的运行方式如下: 1 With a testng.xml file 直接run as test suite 2 With ant 使用ant 3 From the command li ...