Java线程池ThreadPoolExecutor
⽬录
0,Java 线程状态转换
1,Java 线程池的三种创建⽅式
newCacheThreadPool():核⼼线程数是 0,⾮核⼼线程数是 2^31 - 1,没有阻塞队列(不存放任务)适合任务数⽐较密集,但每个任务执⾏时间较短的情况
newFixedThreadPool(n):核⼼线程数是 n,没有⾮核⼼线程,阻塞队列最⼤为 2^31 - 1适⽤于任务量已知,相对耗时的任务
newSingleThreadExecutor():核⼼线程数是 1,没有⾮核⼼线程,阻塞队列最⼤为 2^31 - 1适⽤于多个任务排队执⾏
public static ExecutorService newCachedThreadPool(){
return new ThreadPoolExecutor(
0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(
nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor(){
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1,1,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
可以看到以上三种创建⽅式实际使⽤的都是 ThreadPoolExecutor 类,只是参数不同⽽已。
2,ThreadPoolExecutor 类的原理
ThreadPoolExecutor:基本的线程池实现
ScheduledThreadPoolExecutor:任务调度线程池:带有定时任务的线程池
在『任务调度线程池』功能加⼊之前,可以使⽤ java.util.Timer 来实现定时功能,Timer 的优点在于简单易⽤
但由于所有任务都是由同⼀个线程来调度,因此所有任务都是串⾏执⾏的,同⼀时间只能有⼀个任务在执⾏,前⼀个任务的延迟或异常都将会影响到之后的任务
线程池有五种状态:
RUNNING:正常运⾏状态,可接收新任务,可处理阻塞队列中的任务
SHUTDOWN:不会接收新任务,但会处理阻塞队列剩余任务
STOP:会中断正在执⾏的任务,并抛弃阻塞队列任务
TIDYING:任务全执⾏完毕,活动线程为 0,即将进⼊终结
TERMINATED:终结状态
1,构造⽅法及参数含义
ThreadPoolExecutor 类位于 urrent 包中,其参数含义如下:
public ThreadPoolExecutor(
int corePoolSize,// 核⼼线程数,核⼼线程就是⼀直存在的线程
int maximumPoolSize,// 最⼤线程数,表⽰线程池中最多能创建多少个线程
// ⾮核⼼线程数 = 最⼤线程数 - 核⼼线程数
long keepAliveTime,// 针对⾮核⼼线程⽽⾔,表⽰线程没有任务执⾏时最多保持多久时间会终⽌
// 默认情况下,只有当线程池中的线程数⼤于corePoolSize时,keepAliveTime才会起作⽤
//  当线程池中的线程数⼤于corePoolSize时,如果⼀个线程空闲的时间达到 keepAliveTime,
//  则会终⽌,直到线程池中的线程数不超过corePoolSize
// 但是如果调⽤了 allowCoreThreadTimeOut(boolean) ⽅法
//  在线程池中的线程数不⼤于corePoolSize时,keepAliveTime参数也会起作⽤
//  直到线程池中的线程数为 0
TimeUnit unit,// 时间单位,与 keepAliveTime 配合使⽤,针对⾮核⼼线程
BlockingQueue<Runnable> workQueue,// 存放任务的阻塞队列
ThreadFactory threadFactory,// 创建线程的⼯⼚,可以为线程创建时起个好名字
RejectedExecutionHandler handler // 拒绝策略
/
/ 任务太多的时候会进⾏拒绝操作
// 核⼼线程,⾮核⼼线程,任务队列都放不下时
)
unit 参数有7种取值,在 TimeUni t类中有7种静态属性:
TimeUnit.DAYS;//天
TimeUnit.HOURS;//⼩时
TimeUnit.MINUTES;//分钟
TimeUnit.SECONDS;//秒
TimeUnit.MILLISECONDS;//毫秒
TimeUnit.MICROSECONDS;//微妙
TimeUnit.NANOSECONDS;//纳秒
workQueue 参数表⽰⼀个阻塞队列,⽤来存储等待执⾏的任务,有以下⼏种选择:
ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定⼤⼩
LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列⼤⼩,则默认为 Integer.MAX_VALUE
SynchronousQueue:这个队列⽐较特殊,它不会保存提交的任务,⽽是将直接新建⼀个线程来执⾏新来的任务
handler 参数表⽰拒绝策略,当线程池的任务缓存队列已满,并且线程池中的线程数⽬达到 maximumPoolSize,如果还有任务到来就会采取任务拒绝策略。
handler 通常有以下四种策略:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出 RejectedExecutionException 异常(这是默认策
略)
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但不抛出异常
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前⾯的任务,然后重新尝试执⾏任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调⽤线程(调⽤者)处理该任务
2,⼀些重要⽅法
ThreadPoolExecutor 类中的⼏个重要⽅法:
execute():向线程池提交⼀个任务,交由线程池去执⾏
submit():也是向线程池提交任务,但是和execute()⽅法不同,它能够返回任务执⾏的结果
它实际上还是调⽤的 execute() ⽅法,只不过它利⽤了 Future 来获取任务执⾏结果
invokeAll():提交⼀个任务集合
invokeAny(): 提交⼀个任务集合,哪个任务先成功执⾏完毕,返回此任务执⾏结果,其它任务取消
shutdown():关闭线程池,再也不会接受新的任务
不会⽴即终⽌线程池,⽽是要等所有任务缓存队列中的任务都执⾏完后才终⽌
shutdownNow():关闭线程池,再也不会接受新的任务
⽴即终⽌线程池,并尝试打断正在执⾏的任务,并且清空任务缓存队列,返回尚未执⾏的任务
isShutdown():不在 RUNNING 状态的线程池,此⽅法就返回 true
isTerminated():线程池状态是否是 TERMINATED
动态调整线程池的⼤⼩:
setCorePoolSize:设置 corePoolSize
setMaximumPoolSize:设置 maximumPoolSize
还有⼀些⽅法:
getQueue()
getPoolSize()
getActiveCount()
getCompletedTaskCount()
3,线程池状态
在 ThreadPoolExecutor 中定义了⼀个 volatile 变量,另外定义了⼏个 static final变量表⽰线程池的各个状态:
volatile int runState;// 当前线程池的状态
static final int RUNNING    =0;
static final int SHUTDOWN  =1;
static final int STOP      =2;
static final int TERMINATED =3;
线程池的状态变化:
当创建线程池后,线程池处于 RUNNING 状态
当调⽤了 shutdown() ⽅法,则线程池处于 SHUTDOWN 状态,此时线程池不能够接受新的任务,它会等待所有任务执⾏完毕当调⽤了 shutdownNow() ⽅法,则线程池处于 STOP 状态,此时线程池不能接受新的任务,并且会去尝试终⽌正在执⾏的任务当线程池处于 SHUTDOWN 或 STOP 状态,并且所有⼯作线程已经销毁,任务缓存队列已经清空或执⾏结束后,线程池被设置为TERMINATED 状态
4,线程池模型
ThreadPoolExecutor 类构造出来的线程池的模型如下:
⼤体上由三⼤部分组成:
核⼼线程:⼀直存在的线程,数量为 corePoolSize
在创建了线程池后,默认情况下,线程池中并没有任何线程,⽽是等待有任务到来才创建线程去执⾏任务;当线程池中的线程数⽬达到 corePoolSize 后,就会把到达的任务放到缓存队列当中;
除⾮⼿动调⽤了 prestartAllCoreThreads() 或者 prestartCoreThread() ⽅法,来预创建线程,即在没有任务到来之前就创建线程。
⾮核⼼线程:临时创建的线程,会根据任务的多少来进⾏创建
java线程池创建的四种当然总的⾮核⼼线程的数量不能⼤于 maximumPoolSize - corePoolSize
如果总的⾮核⼼线程的数量很⼤,并且任务⾮常多,就会创建⾮常多的线程
任务队列:
SynchronousQueue:只能存放⼀个任务
LinkedBlockingQueue:可以⽆限存放任务,如果任务超级多,会有内存溢出的可能
同⼀时刻,线程池中所能接纳的最⼤任务数为:maximumPoolSize + 任务队列的长度;
当超出这个范围后,如果再有新的任务过来,将会被拒绝。
线程池中的线程的初始化:
默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程
在实际中,如果需要线程池创建之后⽴即创建线程,可以通过以下两个⽅法办到:
prestartCoreThread():初始化⼀个核⼼线程
prestartAllCoreThreads():初始化所有核⼼线程
处理任务的流程:
如果当前线程池中的线程数⽬⼩于 corePoolSize,每来⼀个任务,就会创建⼀个线程去执⾏这个任务
如果当前线程池中的线程数⽬>=corePoolSize,每来⼀个任务,会尝试将其添加到任务缓存队列当中
若添加成功,则该任务会等待空闲线程将其取出去执⾏
若添加失败(⼀般来说是任务缓存队列已满),则会尝试创建临时线程去执⾏这个任务
当核⼼线程,任务队列,⾮核⼼线程都使⽤完后,如果还有新的任务过来,将会进⾏拒绝处理
线程复⽤: 当线程处理完已分配的任务后,在没有销毁之前,还会⽤于去处理新的任务。这样可以避免创建过多的线程。
3,任务的执⾏过程

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。