线程池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.包装类等)
随机推荐
- C++ 建设者继承
微通道公用号CodingRush 分享编程.算法.机器人leanring.数据挖掘.推荐系统.知识大数据计算框架,欢迎扫码关注. 子类继承了哪些东西? 子类在继承父类的时候,父类的public成员变量 ...
- sql server事物控制
一.多个数据库 1.存储过程 2.Commit写在 Try...Catch后面 protected void Button1_Click(object sender, EventArgs e) ...
- 将本地文件上传到指定的服务器(HttpWebRequest方法)
将本地文件上传到指定的服务器(HttpWebRequest方法),通过文件流,带文件名,同文件一同上传的表单文本域及值. ///<summary> /// 将本地文件上传到指定的服务器(H ...
- 基于JAVA WEB技术旅游服务网站系统设计与实现网上程序代写
基于JAVA WEB技术旅游服务网站系统设计与实现网上程序代写 专业程序代写服务(QQ:928900200) 随着社会的进步.服务行业的服务水平不断发展与提高,宾馆.酒店.旅游等服务行业的信息量和工作 ...
- Linux:闪光的宝石,智慧 (在)
Linux:闪光的宝石,智慧的结晶(上) 老实说,这十几天以来.因为我违反了"家规",又被断网处罚(拔掉网线).没收手机与老年证(不许出家门). 因此.我平日里仅仅能面对一篇文章& ...
- 返璞归真 asp.net mvc (9) - asp.net mvc 3.0 新特性之 View(Razor)
原文:返璞归真 asp.net mvc (9) - asp.net mvc 3.0 新特性之 View(Razor) [索引页][源码下载] 返璞归真 asp.net mvc (9) - asp.ne ...
- Cocos2d-x学习笔记(9)(CCTextFieldTTF使用输入框)
1.CCTextFieldTTF创建和使用 CCTextFieldTTF::create(const char* placeholder,const char* fontName.float font ...
- 【转】Directx11 SDK文档
原文地址:http://blog.csdn.net/cmt100/article/details/6343274 总结 这是一个初步的教程.我们将通过必要的步骤来创建一个Win32 Applicati ...
- BZOJ 2431 HAOI2009 在列的数目的顺序相反 递归
标题效果:乞讨1~n有都布置在物种的数目相反的顺序k计划数 订购f[i][j]对于前者i原子的反向排列的数j计划数 因此,我们将第一i插入的数1~i-1该装置 能生产0~i-1反向对 再就是 f[i] ...
- iphone内容开发技术学习
一.iOS基础 1 开发环境搭建以及IOS组件.框架的概要介绍. 2 mac操作系统与iOS操作系统 3 xcode IDE开发环境的初始 二.C语言基础 1数据类型.表达式与控制流程语句 2数组.函 ...