什么是线程池?
线程池是一种基于池化技术的并发执行模型,用于在多线程编程中有效管理线程的创建、执行、和销毁过程。线程池的核心思想是预先创建一定数量的线程,放入一个池中备用,当有新的任务到来时,就从池中取出一个线程来执行任务,执行完毕后,线程不会被销毁,而是返回线程池中等待下一次任务。
使用线程池的好处包括:
- 降低资源消耗:重复利用已经创建的线程,避免了频繁创建和销毁线程的开销,从而提高系统资源的利用率。
- 提高响应速度:任务到来时,无需等待线程创建即可立即执行,因此可以减少任务的响应时间。
- 提高线程的可管理性:线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性。线程池可以统一分配、调优和监控线程。
- 提供更多灵活和强大的功能:如线程池可以根据系统的负载自动调整线程池中线程的数量,实现动态管理。
线程池通常包含几个关键组成部分:
- 线程池管理器(Pool Manager):用于创建和管理线程池。
- 工作队列(Work Queue):未处理任务的缓存队列,任务在等待被线程池中的线程执行。
- 工作线程(Worker Threads):线程池中的线程。
- 任务接口(Task Interface):每个任务必须实现的接口,以便工作线程可以执行任务。
在Java中,Executor
框架是管理线程池的一种高级实现,提供了各种类型的线程池,如FixedThreadPool
、CachedThreadPool
、SingleThreadExecutor
等,让开发者可以根据实际需求选择最适合的线程池来使用。
线程池的相关接口和实现类
java中常用Executors工具类,来创建常见的线程池。
常见的四种线程池对象
-
Executors.newCachedThreadPool():动态数量的ThreadPoolExecutor线程池对象,线程在存活时间内(60秒)可以重复使用,这个线程池没有核心线程数,同时它的工作队列也是不存放线程任务的
-
Excutors.newFixedThreadPool():固定数量的ThreadPoolExecutor线程池对象,该线程池的最大线程数和核心线程数相同
-
Excutors.newSingleThreadExecutor():这是一个只有一个线程的ThreadPoolExecutor线程池对象
-
Executors.newScheduledThreadPool():这是一个任务调度线程池的ScheduledThreadPoolExecutor线程池对象,因为这个线程池是ScheduledThreadPoolExecutor接口的实现类,所有在实现任务调度上有三种方法:
当使用schedule()方法延迟指定时间后执行线程任务(仅执行一次)。
当使用scheduleAtFixedRate()方法会延迟指定时间后执行线程任务,并且每过指定时间执行一次。
当使用scheduleWithFixedDelay()()方法会延迟指定时间后执行线程任务,并会在上一次任务执行结束后,"延迟"时间后执行
ExecutorService在提交线程任务时的两种方法:
- 通过execute运行Runnable接口接口没有返回值类型
- 通过submit运行Callable接口也可以运行Runnable接口
ExecutorService关闭线程池的两种方法:
- shutdown():已提交的任务将继续执行,但不再接收新的任务
- shutdownNow():终止所有正在执行的任务,并停止处理等待队列中的的任务
线程池的执行流程
如图所示,就是线程池的工作流程,可以分为五个步骤:
- 当有线程任务提交到线程池时,线程池会判断当前线程池中是否有空闲线程,如果存在空闲线程会优先分配空闲线程执行线程任务
- 当线程池中不存在空闲线程时,会判断当前线程池中的线程数是否小于核心线程数,如果小于核心线程数时会创建核心线程,并执行线程任务
- 如果当前线程数大于核心线程数时,线程池会判断当前线程池的工作队列是否已满,如果工作队列未满,会将线程任务加入到工作队列中,当线程池中存在空闲线程时,会按照先进先出的原则依次取出线程任务
- 当线程池的工作队列已满时,会判断当前线程池的最大线程数是否超出,未超出会创建非核心线程,并执行线程任务,当线程任务结束后,非核心线程会被回收
- 当当前线程池的线程数到达最大线程数时,会执行拒绝策略
常用的四种线程池及其特点
public class Test06 {
public static void main(String[] args) {
//创建ThreadPoolExecutor的线程池
//特点:线程池的数量是动态数量(线程会被缓存,空闲线程超过六十秒会被回收)
Executors.newCachedThreadPool();
//创建ThreadPoolExecutor的线程池
//特点:线程池的数量是固定的
Executors.newFixedThreadPool(10);
//创建ThreadPoolExecutor的线程池
//只有一个线程的线程池
Executors.newSingleThreadExecutor();
//创建ScheduledThreadPoolExecutor的线程池
// 这个线程池的工作队列是DelayedWorkQueue
Executors.newScheduledThreadPool(1);
}
}
线程池的核心配置参数
如上图所示就是我们在定义线程池是需要配置的核心参数:
-
int corePoolSize:核心线程数(也叫最小线程数)
-
int maximumPoolSize:最大线程数
-
long keepAliveTime:非核心线程的存活时间
-
TimeUnit unit:存活时间的单位
-
BlockingQueue workQueue:工作队列(阻塞队列,线程安全的队列集合)
-
ThreadFactory threadFactory:线程工厂
-
RejectedExecutionHandler handler:拒绝策略
四种拒绝策略:
AbortPolicy:丢弃当前线程任务,并抛出异常
DiscardPolicy:丢弃当前线程任务,但不抛出异常
DiscardOldestPolicy:丢弃工作队列中最“老”的线程任务
CallerRunsPolicy:直接由“当前调用线程”执行该线程任务
或者自定义拒绝策略:实现RejectedExecutionHandler接口
线程池的状态
在线程池被创建后,线程池处于运行状态,接受新的线程任务,并处理工作队列中的线程任务,当线程池调用shutdown()方法后,线程池变为关闭状态,此状态下的线程池不接受新的线程任务,但仍处理工作队列中的任务,当线程池调用shutdownNow()方法后,线程池变为停止状态,此状态下的线程池不接收新任务,也不处理工作队列中的任务,同时中断正在执行的任务,当所有任务已经结束,工作队列中的任务数量为0时,线程池变为整理状态,当线程池彻底关闭,线程池变为终止状态
线程池的状态:
- RUNNING(运行状态):允许新任务,并处理工作队列中的任务,可以调用shutdown()方法和shutdownNow()方法
- SHUTDOWN(关闭状态):不接收新任务,但仍处理工作队列中的任务
- STOP(停止状态):不接收新任务,也不处理工作队列中的任务,同时中断正在执行的任务
- TIDYING(整理状态):所有任务已经结束,工作队列中的任务数量为0
- TERMINATED(终止状态):线程池彻底关闭
线程池如何保存状态和线程数量:AtomicInteger ctl:前3位保存“线程状态”,后29位保存“线程数量(线程池存活线程数)”