关于
我的项目
相关阅读
热度排行
- [转] 宫崎骏用动漫教给我们的人生哲理,每一句都能说到心里! - (日期:[八月 24, 2013] 点击:[53,223])
- Google 网页爬虫报告无法连接站点解决办法 - (日期:[七月 20, 2014] 点击:[38,641])
- 架设Tiny Tiny RSS(TTRSS)阅读器,找回Google Reader! - (日期:[九月 27, 2013] 点击:[27,769])
- SkyDrive、DropBox和Google Drive三大公有云存储服务对比 - (日期:[六月 25, 2013] 点击:[25,574])
- 升级到至强E5440后,与i5 CPU笔记本性能对比 - (日期:[二月 18, 2014] 点击:[23,713])
- 公钥私钥加密解密数字证书数字签名详解 - (日期:[四月 19, 2014] 点击:[22,959])
- 本站建站技术合集 - (日期:[九月 20, 2013] 点击:[22,490])
- 使用OpenerDNS解决无法访问Google的问题 - (日期:[七月 5, 2014] 点击:[21,789])
- WordPress博客添加“返回顶部”按钮 - (日期:[七月 14, 2013] 点击:[21,203])
- Linux文件系统基础之inode和dentry - (日期:[三月 13, 2015] 点击:[20,167])
- 云存储中的HTTP鉴权算法分析 - (日期:[二月 7, 2014] 点击:[18,640])
- 存储基础知识之——磁盘阵列原理及操作实战 - (日期:[二月 9, 2014] 点击:[17,492])
- 精选37条强大的常用linux shell命令组合 - (日期:[九月 4, 2013] 点击:[17,429])
- DNS原理、架构和配置详解 - (日期:[九月 6, 2013] 点击:[16,803])
- Netty和Jetty的Java NIO 网络框架模型分析 - (日期:[七月 13, 2013] 点击:[16,333])
- CoreOS 初识之安装 - (日期:[十一月 16, 2014] 点击:[16,170])
- Windows与Linux文件系统互访的几种方法 - (日期:[八月 21, 2014] 点击:[15,733])
- Dijkstra算法求解最短路径分析 - (日期:[七月 12, 2014] 点击:[14,924])
- NAS解决方案实现多媒体文件共享播放 - (日期:[十二月 21, 2014] 点击:[13,915])
- 简介 - (日期:[九月 1, 2012] 点击:[13,757])
- 如何编程实现 2 + 2 = 5? - (日期:[六月 2, 2014] 点击:[13,269])
- 搭建了一个iNews程序 - (日期:[十月 15, 2013] 点击:[13,236])
- 2014年9月曝出的Bash ShellShock漏洞简析 - (日期:[九月 26, 2014] 点击:[13,138])
- 彻底解决WordPress博客垃圾评论的问题 - (日期:[八月 5, 2013] 点击:[13,086])
- 如何使用1M的内存排序100万个8位数 - (日期:[三月 27, 2014] 点击:[12,552])
- 全部日志列表 - (日期:[十一月 11, 2012] 点击:[12,328])
- 关于回调函数和this指针探讨 - (日期:[八月 24, 2014] 点击:[12,209])
- 给定一个long型常量,其值为x,给定long型变量a,要求a & x 的取值集合 - (日期:[九月 8, 2012] 点击:[11,703])
- WordPress建站必备实用插件 - (日期:[八月 7, 2014] 点击:[11,360])
- Amazon 云计算业务全面介绍 - (日期:[三月 9, 2014] 点击:[11,268])
分类目录
文章归档
- 2024年四月 (1)
- 2024年二月 (1)
- 2023年九月 (1)
- 2023年一月 (1)
- 2022年十月 (1)
- 2022年八月 (2)
- 2022年四月 (1)
- 2022年三月 (1)
- 2021年十二月 (2)
- 2021年十月 (2)
- 2021年九月 (1)
- 2021年八月 (1)
- 2021年五月 (1)
- 2021年三月 (2)
- 2021年一月 (2)
- 2020年十二月 (5)
- 2020年十一月 (2)
- 2020年十月 (2)
- 2020年九月 (1)
- 2020年八月 (5)
- 2020年七月 (2)
- 2019年九月 (1)
- 2018年八月 (1)
- 2018年七月 (1)
- 2018年六月 (1)
- 2018年五月 (1)
- 2018年三月 (1)
- 2018年二月 (1)
- 2018年一月 (2)
- 2017年十二月 (3)
- 2017年十月 (4)
- 2017年九月 (1)
- 2017年七月 (1)
- 2017年六月 (1)
- 2016年十二月 (1)
- 2016年十月 (1)
- 2016年九月 (1)
- 2016年七月 (2)
- 2016年六月 (1)
- 2016年二月 (3)
- 2015年十二月 (3)
- 2015年十一月 (2)
- 2015年十月 (1)
- 2015年八月 (2)
- 2015年七月 (4)
- 2015年六月 (1)
- 2015年三月 (2)
- 2015年二月 (1)
- 2015年一月 (4)
- 2014年十二月 (2)
- 2014年十一月 (2)
- 2014年十月 (5)
- 2014年九月 (8)
- 2014年八月 (11)
- 2014年七月 (17)
- 2014年六月 (7)
- 2014年五月 (15)
- 2014年四月 (16)
- 2014年三月 (14)
- 2014年二月 (5)
- 2013年十二月 (5)
- 2013年十一月 (3)
- 2013年十月 (13)
- 2013年九月 (13)
- 2013年八月 (13)
- 2013年七月 (9)
- 2013年六月 (8)
- 2013年五月 (1)
- 2013年三月 (3)
- 2013年一月 (1)
- 2012年十一月 (1)
- 2012年九月 (12)
- 2012年八月 (3)
- 2011年二月 (1)
- 2009年三月 (1)
- 2009年二月 (1)
- 2008年十一月 (1)
- 2008年六月 (1)
- 2008年四月 (1)
- 2008年三月 (1)
Java ThreadPoolExecutor线程池使用的一个误区
Java对多线程的封装非常丰富,提供了多种适用于不同场景的多并发实现。其中最基础,最核心的线程池要属ThreadPoolExecutor类了。该类有如下几个构造函数:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
常用的几个参数:corePollsize,maximumPoolSize,workQueue。一般都知道,这三个参数决定了线程池的并发模型。但是具体怎么并发,却很容易混淆。
从字面意思很容易理解成,corePoolSize,是线程池的核心线程数,容易理解成线程池启动时就会创建这个数量的线程待用。而maximumPoolSize,很容易理解成,当提交的任务数大于核心线程数时,线程池会创建更多的线程来执行,直到线程池内的线程数达到maximumPoolSize值时就不会再创建新的线程了。如果还有新提交的任务,那么会入workQueue队列。当有线程空闲下来时,会到队列中取出任务来执行。当线程池队列为空时,并且当前线程池数目大于corePoolSize时,会释放部分线程,直到线程数等于corePoolSize为止。
事实真的如此吗?
我们仔细阅读这几个构造函数的详细说明会发现,上述理解是不正确的。线程池启动时,并不是一开始就创建corePoolSize数量的线程,而是根据提交任务的情况来的,当线程池内地线程数量少于corePoolSize时,提交任务时,会创建新的线程来执行,当线程数等于corePoolSize时,就会将任务放到队列中,如果队列长度不够大,那么,当任务队列满时,继续提交任务,则线程池会继续创建新的线程,此时线程池内地线程数大于corePoolSize。随着任务的提交,线程池内的线程数达到maximumPoolSize,则不再创建新的线程。再有任务提交时,如果队列满,但是线程池内的线程数已经达到最大值,那么提交任务会失败,抛出RejectedExecutionException异常。
上述解释看起来最终结果差异不大,对于参数的限定值最终效果类似,但是过程是完全不一样的,这会导致完全不同的并发执行结果。
针对线程池的正确用法中的解释,当线程池内的线程数达到corePoolSize时,创建新线程的条件并不是所有的corePoolSize个线程都在工作,没有空闲线程,才去创建新的线程,而是任务队列满,任务队列满与线程池内的线程都在工作之间没有必然联系,即,可能当前线程池内有空闲线程,但是由于队列不够大,很容易满,会导致不断创建新的线程,并且最终导致提交任务时收到rejected异常。
实测,的确存在该场景,因为workQueue是线程安全的同步阻塞队列,能支持的瞬时并发度有限,因此很容易让线程池中的队列积累一定数量的任务。这样,会导致实际的并发数没有达到maximumPoolSize时就会出现提交任务异常,而线程池中又有部分线程处于空闲状态。
因此,如果希望严格控制线程池的并发数量,应该尽量使用corePoolSize来控制,maximumPoolSize设置值与corePoolSize一致。如果使用者在不同的业务场景下,希望并发度不一样,在不需要更大规模的线程池时,则要自行调用ThreadPoolExecutor类的setCorePoolSize函数减少核心线程数,在并发执行任务需求大时,应该增大corePoolSize的数值。
setCorePoolSize
public void setCorePoolSize(int corePoolSize)Sets the core number of threads. This overrides any value set in the constructor. If the new value is smaller than the current value, excess existing threads will be terminated when they next become idle. If larger, new threads will, if needed, be started to execute any queued tasks.
- Parameters:
corePoolSize
– the new core size- Throws:
IllegalArgumentException
– ifcorePoolSize < 0
- See Also:
getCorePoolSize()
来修改线程池内的线程数,并合理设置keepalivetime,让系统回收空闲线程。
另外线程池中的其他几个参数虽然使用不多,但也要注意含义:
ThreadFactory,线程创建工厂,只有一个接口,创建一个新的线程Thread实例,供线程池内需要创建新的线程时回调。一般再此定义线程名字等属性。
RejectedExecutionHandler,当出现rejected异常时,用于处理该异常的handler,线程池提供了三个默认实现,我们也可以自行继承该接口实现被拒绝任务的处理方式。