为什么要使用线程池?
当某个线程类的所需运行时间短且需频繁启动时,应用程序需要频繁的创建线程、销毁线程、线程切换。单个线程的创建、销毁消耗的系统cpu、内存等资源很少,但庞大的数量将耗费很多的资源,且频繁的线程切换也将消耗许多的cpu资源。
使用线程池的好处
- 某个线程执行完任务后不会被销毁,而是等待执行下一个任务,这样线程得以复用,也就没有频繁创建、销毁线程的消耗。
- 当任务到达时不用创建线程,由线程池中的线程执行,因此响应时间更短
- 线程在线程池中便于管理、监控、调优
线程池原理
关键类ThreadPoolExecutor
uml图
Executor
:接口,只定义了void execute(Runnable command)方法。
ExecutorService
: 接口,继承了Executor,增加了shutdown、shutdownNow、submit等方法。
AbstractExecutorService
:抽象类,实现了ExecutorService的submit等方法。
ThreadPoolExecutor
:继承了AbstractExecutorService,实现了execute、shutdown、shutdownNow等方法。
ScheduledThreadPoolExecutor
:继承ThreadPoolExecutor、实现ScheduledExecutorService接口,在ThreadPoolExecutor的基础上实现了ScheduledExecutorService的schedule系列方法。
相关方法解释
execute
:运行Runnable任务,无返回值。
submit
:运行任务,底层调用execute,返回Future对象。
shutdown
:线程池状态设置为SHUTDOWN,不再接受新的任务,完成正在执行的任务,完成缓存队列中的任务,之后线程池状态设置为TERMINATED。
shutdownNow
:线程池状态设置为STOP,不再接受新的任务,终止正在执行的任务,丢弃缓存队列中的任务,之后线程池状态设置为TERMINATED。
ThreadPoolExecutor构造器
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize
:核心线程池大小,创建的ThreadPoolExecutor默认未创建线程,调用prestartAllCoreThreads创建corePoolSize个线程。
maximumPoolSize
: 线程池最大线程数,线程池线程数上限。
keepAliveTime
:当线程数大于corePoolSize时,某个线程空闲时间达到keepAliveTime时将被销毁,直到线程数不大于corePoolSize。当corePoolSize等于maximumPoolSize时,keepAliveTime无作用。
unit
:keepAliveTime的时间单位,有MILLISECONDS、SECONDS等。
workQueue
:缓存队列,当线程数等于corePoolSize,且这些线程都有任务执行时,新的任务存入workQueue,等待执行。
常见BlockQueue
ArrayBlockingQueue
: 有界阻塞队列,创建后不能改变容量
LinkedBlockingQueue
: 可选有界阻塞队列,空参构造出的LinkedBlockingQueue大小为Integer.MAX_VALUE。
SynchronousQueue
: 容量为零
threadFactory
:创建线程的工厂。
handler
:当线程数等于maximumPoolSize时,新的任务将被拒绝处理器handler处理。
拒绝处理器(拒绝策略)
AbortPolicy
: 丢弃任务,抛出RejectedExecutionException。DiscardPolicy
: 仅丢弃任务DiscardOldestPolicy
: 丢弃缓程数冲队列中最老的任务,对新任务调用execute方法。CallerRunsPolicy
: 调用拒绝处理器的调用者所在的线程执行任务。
这些拒绝处理器的源码都很简单。通过实现RejectedExecutionHandler接口,可以自定义拒绝处理器。
线程池提交任务的流程:
Executors的一些静态方法
newFixedThreadPool
: 固定大小线程池,线程数固定为nThreads,缓冲队列大小为Integer.MAX_VALUE。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
newSingleThreadExecutor
: 单个线程,缓冲队列大小为Integer.MAX_VALUE。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
newCachedThreadPool
: 线程池提交新任务,若池中的线程均在执行任务,创建一个线程执行新任务。当某线程空闲时间达到60秒时,销毁该线程。可根据处理需要灵活调整线程数。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
}
newScheduledThreadPool
: 可定时执行任务,可周期执行任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
ScheduledThreadPoolExecutor
继承ThreadPoolExecutor、实现ScheduledExecutorService接口,在ThreadPoolExecutor的基础上实现了ScheduledExecutorService的schedule系列方法。可定时执行任务,可周期执行任务。
ForkJoinPool
ForkJoinPool用于运行ForkJoinTask的ExecutorService。ForkJoinPool是一个用于并行处理任务的框架,不断把大的任务fork成小的任务直至可执行,之后并行执行,小的任务join把结果合并起来。ForkJoinPool的默认工作线程数是cpu的核数,每个工作线程都对应一个双端工作队列。ForkJoinPool的一个特点是采用了工作窃取(work-stealing),线程执行完自己的工作队列中的任务时,线程会随机从其他线程的工作队列的头部窃取任务来执行。