ThreadPoolExecutor类可设置的参数主要有:
- corePoolSize
1.核心线程数,核心线程会一直存活,即使没有任务需要处理。
2.即使现有的线程空闲,也会优先创建新的线程来处理任务,而不是交给现有的线程来处理。
3.核心线程会在allowCoreThreadTimeOut为true时会超时退出,默认情况下不会退出。
- maxPoolSize
1.当线程数大于或等于核心线程数,且任务队列已满时,线程池会创建新的线程,直到数量达到maxPoolSize。
2.如果线程池数量已等于maxPoolSize,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。
keepAliveTime
1.当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize
2.如果allowCoreTimeOut为true时,那么所有线程都会退出直到线程数量为0
allowCoreTimeOut
是否允许核心线程退出,默认值是false
queueCapacity
任务队列容量。从maxPoolSize的描述可以看出,任务队列容量的变化也会影响到线程的变化,因此,任务队列的容量也需要恰当的设置
线程按以下行为执行任务
1.当线程数小于核心线程数时,创建线程
2.当线程数大于或等于核心线程数时,且任务队列未满时,将任务放进任务队列
3.当线程数大于或等于核心线程数时,且任务队列已满时
1)当线程数小于最大线程数时,创建线程
2)当线程数大于最大线程数时,拒绝任务,抛出异常
先来讲讲参数最多的那个构造方法,主要是对那几个烦人的参数进行分析。
1、ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
这里是7个参数(我们在开发中用的更多的是5个参数的构造方法),OK,那我们来看看这里七个参数的含义:
corePoolSize 线程池中核心线程的数量
maximumPoolSize 线程池中最大线程数量
keepAliveTime 非核心线程的超时时长,当系统中非核心线程闲置时间超过keepAliveTime之后,则会被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则该参数也表示核心线程的超时时长
unit 第三个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等
workQueue 线程池中的任务队列,该队列主要用来存储已经被提交但是尚未执行的任务。存储在这里的任务是由ThreadPoolExecutor的execute方法提交来的。
threadFactory 为线程池提供创建新线程的功能,这个我们一般使用默认即可
handler 拒绝策略,当线程无法执行新任务时(一般是由于线程池中的线程数量已经达到最大数或者线程池关闭导致的),默认情况下,当线程池无法处理新线程时,会抛出一个RejectedExecutionException。
emmmmm....看到那么多烦人的概念,是不是有点头大了,我反正是头大了。
这7个参数中,平常最多用到的是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue.在这里我主抽出corePoolSize、maximumPoolSize和workQueue三个参数进行详解。
maximumPoolSize(最大线程数) = corePoolSize(核心线程数) + noCorePoolSize(非核心线程数);
(1)当currentSize<corePoolSize时,没什么好说的,直接启动一个核心线程并执行任务。
(2)当currentSize>=corePoolSize、并且workQueue未满时,添加进来的任务会被安排到workQueue中等待执行。
(3)当workQueue已满,但是currentSize<maximumPoolSize时,会立即开
启一个非核心线程来执行任务。
(4)当currentSize>=corePoolSize、workQueue已满、并且currentSize>maximumPoolSize时,调用handler默认抛出RejectExecutionExpection异常。
什么currentSize,corePoolSize,maximumPoolSize,workQueue比来比去的都比迷糊了,哈哈,那我举个烧烤店的例子来想必大家理解起来更快。
夏天了,很热,所以很多烧烤店都会在外面也布置座位,分为室内、室外两个地方可以吃烧烤。(室内有空调电视,而且室内比室外烧烤更加优惠,而且外面下着瓢泼大雨所以顾客会首先选择室内)
corePoolSize(烧烤店室内座位),cuurentPoolSize(目前到烧烤店的顾客数量),maximumPoolSize(烧烤店室内+室外+侯厅室所有座位),workQueue(烧烤店为顾客专门设置的侯厅室)
第(1)种,烧烤店人数不多的时候,室内位置很多,大家都其乐融融,开心的坐在室内吃着烧烤,看着世界杯。
第(2)种,生意不错,室内烧烤店坐无空席,大家都不愿意去外面吃,于是在侯厅室里呆着,侯厅室位置没坐满。
第(3)种,生意兴隆,室内、侯厅室都坐无空席,但是顾客太饿了,剩下的人没办法只好淋着雨吃烧烤,哈哈,好可怜。
第(4)种,生意爆棚,室内、室外、侯厅室都坐无空席,在有顾客过来直接赶走。
哈哈是不是很形象,对于workQueue还是有点陌生的小伙伴。
推荐博客:http://blog.youkuaiyun.com/u012702547/article/details/52259529
2、其他线程池的记法
剩下的那四种主要的线程池大概思路,用法在我推荐的博客里都有详细解说,在这里我就不一一道来了,在这里主要是跟大家分享一种特别容易记住这四种线程池的方法,在大家写代码,面试时可以即使想到这四种线程池。
(1)FixedThreadPool:
Fixed中文解释为固定。结合在一起解释固定的线程池,说的更全面点就是,有固定数量线程的线程池。其corePoolSize=maximumPoolSize,且keepAliveTime为0,适合线程稳定的场所。
(2)SingleThreadPool:
Single中文解释为单一。结合在一起解释单一的线程池,说的更全面点就是,有固定数量线程的线程池,且数量为一,从数学的角度来看SingleThreadPool应该属于FixedThreadPool的子集。其corePoolSize=maximumPoolSize=1,且keepAliveTime为0,适合线程同步操作的场所。
(3)CachedThreadPool:
Cached中文解释为储存。结合在一起解释储存的线程池,说的更通俗易懂,既然要储存,其容量肯定是很大,所以他的corePoolSize=0,maximumPoolSize=Integer.MAX_VALUE(2^32-1一个很大的数字)
(4)ScheduledThreadPool:
Scheduled中文解释为计划。结合在一起解释计划的线程池,顾名思义既然涉及到计划,必然会涉及到时间。所以ScheduledThreadPool是一个具有定时定期执行任务功能的线程池。
三、线程池的单例
容我伸个懒腰,该讲本章重点内容了,在此之前,我们对基本语意知识进行了解一下。
什么是单例呢?咳咳。
1、单例
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意事项:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
推荐:http://www.runoob.com/design-pattern/singleton-pattern.html
2、线程池的单例
那么问题来了,我线程池用的好好的,用的时候创建一个,不用就不管他,那为什么要将线程池设计成单例模式呢。那么就要看看你将线程池应用的场所了。一般情况下,整个系统中只需要单种线程池,多个线程公用一个线程池,不会是每创一个线程就要创建一个线程池,那样子你还不如不用线程池呢。
言归正传,咱们来看看如何将线程池设计成单例模式。废话少说上代码
首先在ThreadPool类里面实现线程池的创建,我们这里创建的是FixedThreadPool线程池(记住构造方法要私有,保证不被其他类实例化)
private ThreadPool(int corepoolsize, int maximumpoolsize, long keepalivetime){
this.corepoolsize = corepoolsize;
this.maximumpoolsize = maximumpoolsize;
this.keepalivetime = keepalivetime;
}
public void executor(Runnable runnable){
if (runnable == null){
return;
}
if (mexecutor == null){
mexecutor = new ThreadPoolExecutor(corepoolsize, //核心线程数
maximumpoolsize, //最大线程数
keepalivetime, //闲置线程存活时间
TimeUnit.MILLISECONDS, // 时间单位
new LinkedBlockingDeque<Runnable>(), //线程队列
Executors.defaultThreadFactory(), //线程工厂
new ThreadPoolExecutor.AbortPolicy() //队列已满,而且当前线程数已经超过最大线程数时的异常处理策略
);
}
mexecutor.execute(runnable);
}
再然后对ThreadPool内部类,在类里面对他实例化,实现单例
// 获取单例的线程池对象
public static ThreadPool getThreadPool() {
if (mThreadPool == null) {
synchronized (ThreadManager.class) {
if (mThreadPool == null) {
int cpuNum = Runtime.getRuntime().availableProcessors();// 获取处理器数量
int threadNum = cpuNum * 2 + 1;// 根据cpu数量,计算出合理的线程并发数
mThreadPool = new ThreadPool(threadNum, threadNum, 0L);
}
}
}
return mThreadPool;
}