线程池ThreadPoolExecutor继承自ExecutorService。是jdk1.5加入的新特性,将提交执行的任务在内部线程池中的可用线程中执行。 构造函数
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize
线程池维护的核心线程数。
为什么这里说核心线程数而不是最小线程数是因为在线程池被创建后,并不会直接创建corePoolSize个线程,而是等任务到来时临时创建。等按照需要创建了corePoolSize个线程之后,这些数量的线程即使闲置,也不会被线程池收回。这时即可以将这个值理解为线程池维护的最小线程数了。
maximumPoolSize
线程池维护的最大线程数。
keepAliveTime
当线程池中的线程数量大于corePoolSize,多出那部分数量的线程空闲keepAliveTime后会被收回。
uni
keepAliveTime的时间单位。可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue
存放通过execute方法提交给线程等待执行的任务的队列。
threadFactory
负责给线程池创建线程的工厂。
handler
在当队列达到极限导致任务执行阻塞时执行的处理策略。handler有四个选择:ThreadPoolExecutor.AbortPolicy(抛出java.util.concurrent.RejectedExecutionException异常)。ThreadPoolExecutor.CallerRunsPolicy(重试添加当前的任务,他会自动重复调用execute方法)。ThreadPoolExecutor.DiscardOldestPolicy(抛弃旧的任务)。 ThreadPoolExecutor.DiscardPolicy(抛弃当前的任务)。 对于一般的应用,我们不用通过构造函数来创建线程池,而是用一些封装过的工具方法,这些方法设置了大多数参数的缺省值。只有对线程池的特性有更高的要求时,才直接使用构造函数。 Executors.newCachedThreadPool(创建自动增加或减少容量的线程池) Executors.newFixedThreadPool(创建固定容量的线程池) Executors.newSingleThreadExecutor(单独线程的线程池)。 下面是一些高级特性 corePoolSize和maximumPoolSize ThreadPoolExecutor会根据corePoolSize和maximumPoolSize的值调整线程池中线程的数量。当通过ThreadPoolExecutor.execute方法向线程池提交一个新的任务时,如果线程池当前线程数量小于corePoolSize,就算有线程空闲,也会在创建一个线程执行这个任务;如果线程池当前线程数量大于corePoolSize又小于maximumPoolSize,只有当可用线程不够的时候才会创建新的线程。如果不希望系统动态增减线程数量,则将corePoolSize和maximumPoolSize数值设置为一样的值。如果将maximumPoolSize设置为一个特别大的值如Integer.MAX_VALUE,则ThreadPoolExecutor成为了一个能够容纳大量并发任务的线程池。一般来说corePoolSize和maximumPoolSize是在构造ThreadPoolExecutor对象时设置好的,当仍然可以调用ThreadPoolExecutor.setCorePoolSize 和ThreadPoolExecutor.setMaximumPoolSize 方法修改这两个属性。
关于线程创建 线程池中的线程是由ThreadFactory创建。如果不特别指定,会使用Executors.defaultThreadFactory创建位于同一个线程组,相同优先级(NORM_PRIORITY)的非守护线程。如果由你来指定ThreadFactory,你可以定制线程名字,线程组,优先级,是否为守护线程等属性。
直到需要时才开始创建线程 缺省情况下,在线程池刚刚创建好之后其中是没有任何线程存在的。直到向线程池中提交了任务。可以通过复写ThreadPoolExecutor.perstartCoreThread或ThreadPoolExecutor.prestartAllCoreThreads方法改变默认行为。
线程的存活时间 如果线程池中当前线程数大于corePoolSize,如果超过keepAliveTime后仍然没有使用,则超出部分会被终止掉。当需要更多线程的时候,再重新创建。这个属性也可以通过setKeepAliveTime修改。
队列 BlockingQueue用来管理被提交进来等待执行的任务。是否进入queue和线程池的容量有关。如果线程池中运行的线程小于corePoolSize,新的任务不会进入queue而是在新建的线程中执行。如果线程池中运行的线程大于或等于corePoolSize,更倾向于将任务加入queue而不是新建线程。如果queue满了,则创建新的线程执行任务。如果线程数量大雨了maximumPoolSize,任务被拒绝。 以下是三种列队的策略: 1.直接传递:这也是缺省的实现使用SynchronousQueue,直接将队列中的任务转交给线程。如果将任务提交给队列时没有足够的线程处理这个任务,新的线程会被创建。一般来说需要将maximumPoolSize设置为最大以避免出现拒绝新提交来的任务的情况出现。当然了如果任务提交的速度大过了处理任务的速度会引起线程池中线程无限增长的问题。 2.无限队列:使用不限制容量的队列LinkedBlockingQueue。当所有corePoolSize的线程都在忙碌时新提交进来的任务在队列中等待。也就是说,即使线程池中的线程数小于maximumPoolSize,也不会有新的线程被创建(maximumPoolSize参数失效)。这种策略适用于提交来的各个任务相互独立的场景。例如,一个网页服务来说,使用这种策略能平滑瞬间突发的访问请求。 3.有限队列:使用有限队列防止将maximumPoolSize设置为最大时,资源耗尽的问题。调整队列大小和maximumPoolSize之间关系比较就变得重要了。使用大容量队列和较小的线程数可以降低CPU和资源的使用但会导致效率低下。小容量的队列需要相对较大的maximumPoolSize配合,增加了CPU调度线程的负担。 · 任务拒绝
调用ThreadPoolExecutor.execute方法提交新任务时,如果线程池已经被停止运行或线程数量、队列数量已满,RejectedExecutionHandler.rejectedExecution被调用。
=============================================================================================
上面说的都有点抽象,确实得在使用的时候细细研究才成。不过如果想做到遇到问题将其信手拈来,还是有必要记住这个线程池处理的基本逻辑的。下面就举个现实生活中的例子帮助理解一下。 我们可以把一个线程池(ThreadPoolExecutor)想成公司的一个部门 A(Executors.newCachedThreadPool):这个部门负责处理从客户那里分配过来的任务(Task)。在部门刚刚建立之初人力资源部设定了部门人员的上限(maximumPoolSize=60)和下限(corePoolSize=0),并要求部门经理等到第一个任务到来的时候才可以招收第一个组员(直到需要时才创建线程)。部门还制定了一个自己的工作制度,那就是我们绝不能够让任务积压!部门一成立,任务便从客户那边分配过来,部门经理为每一个任务招收一名员工。当员工完成手头的工作,部门经理为他们放假一段时间(keepAliveTime)进行休息。经过休整回来的员工,精神满满的等待接受经理给你派发的任务继续快乐的工作。可是这段时间经济不景气,没有那么多工作需要完成,处于节约成本的考虑,公司要把没有活干的非核心员工(由于corePoolSize=0,所以部门没有核心员工)解雇。到了年底,工作越来越多,部门经理继续招人,直到部门人数达到了上限,每一个员工都在拼命地工作没有一丝喘息的机会。这时又一个任务分配给了这个命苦的部门,也就是这个任务成为了击垮部门的最后一根稻草。部门经理受不了了,决定抗议:我们干不了更多的工作了(AbortPolicy)! B(Executors.newFixedThreadPool):这个部门负责处理从客户那里分配过来的任务(Task)。在部门刚刚建立之初人力资源部设定了部门人员的下限(corePoolSize)。并要求部门经理等到第一个任务到来的时候才可以招收第一个组员(直到需要时才创建线程)。部门还制定了一个自己的工作制度,那就是我们绝不能够让任务积压的太多(LinkedBlockingQueue.capacity)!部门一成立,任务便从客户那边分配过来,部门经理为每一个任务招收一名员工。一直等到部门人数达到部门人员的下限就不能再招人了。多余的任务就要搁置在任务积压列表中,等其他任务完成后再逐个完成。由于这个部门都是核心员工,所以大不用担心被解雇的问题了。当分配给部门的任务打过了积压任务的上限时,部门经理调出来说:我们干不了更多的工作了(AbortPolicy)!这个部门的工作人员下限可以设置,极端情况下部门只有一个员工(Executors.newSingleThreadExecutor)。 后来公司觉得,如果派发给部门的任务过多时,部门经理仅仅跳出来喊一声拒绝有点太过简单,还应该允许他做更多的事情化解危机。于是部门经理逐渐学会了ThreadPoolExecutor.CallerRunsPolicy(重试添加当前的任务,他会自动重复调用execute方法)。ThreadPoolExecutor.DiscardOldestPolicy(抛弃旧的任务)。 ThreadPoolExecutor.DiscardPolicy(抛弃当前的任务)。 由此可见,ThreadPoolExecutor线程池,兼顾了使用的方便性和扩展的灵活性。对于一般用途,借助Executors足以。若是将ThreadFactory ,RejectedExecutionHandler,BlockingQueue的不同实现灵活组合,又能满足各种情况下的需求真不枉为大家之作。
================================================================================================
顺便介绍一下,多线程大师Doug Lea。也就是java.util.concurrent包的作者。
如果IT的历史,是以人为主体串接起来的话,那么肯定少不了Doug Lea。这个鼻梁挂着眼镜,留着德王威廉二世的胡子,脸上永远挂着谦逊腼腆笑容,服务于纽约州立大学Oswego分校计算器科学系的老大爷。 说他是这个世界上对Java影响力最大的个人,一点也不为过。因为两次Java历史上的大变革,他都间接或直接的扮演了举足轻重的脚色。一次是由JDK 1.1到JDK 1.2,JDK1.2很重要的一项新创举就是Collections,其Collection的概念可以说承袭自Doug Lea于1995年发布的第一个被广泛应用的collections;一次是2004年所推出的Tiger。Tiger广纳了15项JSRs(Java Specification Requests)的语法及标准,其中一项便是JSR-166。JSR-166是来自于Doug编写的util.concurrent包。 值得一提的是: Doug Lea也是JCP (Java小区项目)中的一员。 Doug是一个无私的人,他深知分享知识和分享苹果是不一样的,苹果会越分越少,而自己的知识并不会因为给了别人就减少了,知识的分享更能激荡出不一样的火花。《Effective JAVA》这本Java经典之作的作者Joshua Blosh便在书中特别感谢Doug是此书中许多构想的共鸣板,感谢Doug大方分享丰富而又宝贵的知识。
http://www.cnblogs.com/abbuggy/archive/2011/06/16/2594251.html
- JUC学习笔记--从阿里Java开发手册学习线程池的正确创建方法
前言 最近看阿里的 Java开发手册,上面有线程池的一个建议: [强制]线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式, 这样的处理方式让写的同学 ...
- 从阿里Java开发手册学习线程池的正确创建方法
前言 最近看阿里的 Java开发手册,上面有线程池的一个建议: [强制]线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更 ...
- 【转】线程池体系介绍及从阿里Java开发手册学习线程池的正确创建方法
jdk1.7中java.util.concurrent.Executor线程池体系介绍 java.util.concurrent.Executor : 负责线程的使用与调度的根接口 |–Execut ...
- Java核心知识点学习----线程中如何创建锁和使用锁 Lock,设计一个缓存系统
理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...
- Windows API学习---线程与内核对象的同步
前言 若干种内核对象,包括进程,线程和作业.可以将所有这些内核对象用于同步目的.对于线程同步来说,这些内核对象中的每种对象都可以说是处于已通知或未通知的状态之中.这种状态的切换是由Microsoft为 ...
- Java核心知识点学习----线程同步工具类,CyclicBarrier学习
线程同步工具类,CyclicBarrier日常开发较少涉及,这里只举一个例子,以做备注.N个人一块出去玩,相约去两个地方,CyclicBarrier的主要作用是等待所有人都汇合了,才往下一站出发. 1 ...
- Java核心知识点学习----线程中的Semaphore学习,公共厕所排队策略
1.什么是Semaphore? A counting semaphore. Conceptually, a semaphore maintains a set of permits. Each acq ...
- JAVA Excel API学习案例
先贴代码吧,执行一下.看看效果,然后看看凝视,再看看代码后面的基础介绍 创建一个新excel并写入数据: public static void myExcel2() throws IOExceptio ...
- java核心API学习
1:java.lang (Object.String.StringBuffer.Thread.System.ClassLoader.Class.Runtime.包装类等)
随机推荐
- Windows+Atlassian-Jira-6.0.4+MySql5.0安装破解汉化
Windows+Atlassian-Jira-6.0.4+MySql5.0安装破解汉化 一:整理的安装程序 例如以下图: 文件太大.上传不到csdn上.有须要的联系. 新增的百度云盘下载:链接: ...
- 采用Eclipse中间Maven构建Web项目错误(一)
采用Eclipse中间Maven构建Web工程 1.在进行上述操作时.pom.xml一直报错 <project xmlns="http://maven.apache.org/POM/4 ...
- JAVA环境配置---------jdk安装路径中存在空格的问题
我把jdk 安装在C:\Program Files下 而ide启动脚本 startup.bat 例如以下: if "%OS%"=="Windows_NT" se ...
- NYOJ 745 蚂蚁问题(两)
蚂蚁的难题(二) 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描写叙述 下雨了,下雨了.蚂蚁搬家了. 已知有n种食材须要搬走,这些食材从1到n依次排成了一个圈.小蚂蚁对每种 ...
- NServiceBus开发
使用NServiceBus开发分布式应用 系列主题:基于消息的软件架构模型演变 NServiceBus 是一个.Net平台下开源的消息服务框架,这类产品有时也被称作ESB(Enterprise Ser ...
- Android - 分享内容
Android程序一个很重要地特性是他们可以相互通讯和交互.为什么要重新开发那些不是你程序的核心而且其他程序已经有的功能呢? 这里将要介绍一些使用Intent API和ActionProvider对象 ...
- 动软.NET 分页存储过程UP_GetRecordByPage
1, ------------------------------------ --用途:支持任意排序的分页存储过程 --说明: ----------------------------------- ...
- 最少拦截系统(杭电1257)(DP)+(贪心)
最少拦截系统 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Sub ...
- MapReduce源代码分析MapTask分析
前言 MapReduce该分析是基于源代码Hadoop1.2.1代码分析进行的基础上. 该章节会分析在MapTask端的详细处理流程以及MapOutputCollector是怎样处理map之后的col ...
- 【PHP】PHP获得第一章
一,PHP上部和下部壳体敏感 1)所有的用户定义的函数.类和keyword敏感. 例如以下结果输出一致: echo "hello world" Echo "hello ...