前言
在平时的开发中,经常遇到这样的东西,例如数据库连接池,web请求也是使用的池化技术,只是我们直接接触的比较少而已;
正文
什么是线程池
简单点理解就是装线程的一个池子,一种池化思想对一定数量的线程进行管理,如:线程创建,销毁,执行任务等;
线程池流程
- 提交任务至线程池
- 判断线程池的核心线程数是否已满,未满则创建核心线程来处理该任务,否则进入下一流程
- 判断线程池的队列数是否已满,未满则将该任务加入等待队列中,否则进入下一流程
- 判断线程池的最大线程数是否已满,未满则创建非核心线程处理任务,否则进入下一流程
- 根据线程池配置的拒绝策略对任务进行处理
流程图如下:
线程池
ThreadPoolExecutor
通过java中的ThreadPoolExecutor
来创建线程池,参数如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {}
参数说明:
-
corePoolSize
- 线程池中保留的线程数,即便线程是空闲的,除非设置
allowCoreThreadTimeOut
- 提交任务后,线程池中的线程才会开始创建,如果调用了线程池的
prestartAllCoreThreads()
方法,线程池会提前把核心线程都创造好,并启动
- 线程池中保留的线程数,即便线程是空闲的,除非设置
-
maximumPoolSize
- 线程池允许创建的最大线程数量,当线程池配置的队列满了以后,会继续创建非核心线程,直到最大线程数量为止
-
keepAliveTime
- 当线程数大于核心线程数时,空闲(非核心)线程的存活时间,当超过这个时间后,空闲线程会被回收
-
unit
keepAliveTime
的时间单位
-
workQueue
- 工作队列,在任务执行前保存任务,该队列只保存由
execute
方法提交的Runnable
任务
- 工作队列,在任务执行前保存任务,该队列只保存由
-
threadFactory
- 线程创建工厂,当执行创建新的线程时该工厂会被使用
- 一般在此设置线程的名字(实现
ThreadFactory
接口的newThread
方法)
-
handler
- 饱和策略,主要有四种
- 当工作队列和线程数达到最大时,会使用该策略
4种拒绝策略:
- AbortPolicy:抛出异常,默认选项
- DiscardPolicy:直接丢弃任务
- DiscardOldestPolicy: 丢弃队列中最老的任务,将新任务保存
- CallerRunsPolicy:返回给调用线程处理该任务
自定义拒绝策略:
定义一个类,实现RejectedExecutionHandler
接口的rejectedExecution
方法就可以了
4种阻塞队列:
- ArrayBlockingQueue :基于数组结构的有界限阻塞队列,初始化是指定容量,先进先出
- LinkedBlockingQueue :基于链表结构的无界限阻塞队列,用此队列,那线程池就达不到最大线程数了
- SynchronousQueue : 同步阻塞队列;每次插入队列必须等待另一个线程的移除操作,反之亦然;
- PriorityBlockingQueue:优先级队列
- DelayedWorkQueue : 延迟队列,为
ScheduledThreadPoolExecutor
静态类
ThreadPoolExecutor运行状态
ThreadPoolExecutor有以下几种运行状态:
- RUNNING : 接收新的任务,并且处理队列中的任务
- SHUTDOWN :不接受新的任务,但是处理队列中的任务
- STOP :不接受新的任务,且不处理队列中的任务,并且中断正在执行中的任务
- TIDYING :所有的任务都结束,线程数量为零,线程池状态转为
TIDYING
将会运行terminated
方法 - TERMINATED :
terminated
方法执行完毕
流程图如下:
创建线程的几种方式
以下的几种创建线程池的方式都使用的是ThreadPoolExecutor
来创建线程池;
- Executors.newCachedThreadPool();
源码如下:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
-
核心线程为0
-
最大线程数为Integer.MAX_VALUE
-
空闲线程存活时间为60秒
-
使用SynchronousQueue队列
- Executors.newFixedThreadPool(15);
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- 核心线程和最大线程数相等
- 空闲线程存活时间为0
- 使用无界阻塞队列
- Executors.newScheduledThreadPool(1); 定时任务线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
- 核心线程数为corePoolSize,最大线程数为Integer.MAX_VALUE
- 空闲线程存活时间为0
- 使用内部延时队列DelayedWorkQueue
- Executors.newSingleThreadExecutor(); 只有一个核心线程,且最大线程为核心线程
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- 核心线程数和最大线程数一致
- 空闲线程存活时间为0
- 使用无界阻塞队列LinkedBlockingQueue
线程池监控
线程池自带的方法:
以上图中的方法可以查看线程池运行时的内部情况;除此之外,ThreadPoolExecutor内部还有三个方法可以去自定义:
-
void beforeExecute(Thread t, Runnable r)
该方法会在任务执行前执行 -
void afterExecute(Runnable r, Throwable t)
该方法在任务执行完后执行,即便是任务出错; -
void terminated()
线程池终止是会执行该方法,例如调用shutdown(),shutdownNow(),remove方法等等;
最后
这篇文章只是对线程池的一些基本信息进行了说明;