一.为什么使用线程池?
如果不使用类似线程池的容器,每当我们需要执行用户任务的时候都要去创建新的线程,线程执行完成之后,线程就会被回收了,这样频繁的创建和小会线程池会浪费大量的系统资源。
二.使用线程池的优点?
线程池通过线程复用机制,并对线程进行统一管理
1.降低系统资源消耗,通过复用已经存在的线程,降低线程创建和销毁造成的消耗
2.提高响应速度,当有任务到达时,无需等待新线程的创建就可以立即执行
3.提高线程的可管理行,线程是稀缺资源,如果无限制的创建,不仅会消耗大量系统资源,还会降低系统的稳定性,使用线程池可以进行对线程进行统一分配,调优和监控。
核心组件
Java线程池的核心实现是ThreadPoolExecutor
类,主要包含以下几个核心组件:
-
工作线程(Worker): 实际执行任务的线程
-
任务队列(BlockingQueue): 存放待执行的任务
-
线程工厂(ThreadFactory): 用于创建新线程
-
拒绝策略(RejectedExecutionHandler): 当任务无法被执行时的处理策略
Java中的线程池实现
Java在java.util.concurrent包中提供了多种线程池的实现,其中最核心的是ExecutorService接口。ExecutorService提供了丰富的线程池管理功能,包括任务的提交、任务的取消、获取任务的执行结果等。Java通过Executors工厂类提供了几种常用的线程池实现:
FixedThreadPool:固定大小的线程池,线程数量在初始化时设定,且不会改变。
CachedThreadPool:可缓存的线程池,线程数量不固定,根据需要动态地创建和销毁线程。
SingleThreadExecutor:单线程的线程池,保证所有任务在同一线程中按顺序执行。
ScheduledThreadPool:支持定时或周期性任务的线程池。
线程池的工作流程
线程池的工作流程大致如下:
任务提交:通过调用ExecutorService的submit()或execute()方法提交任务。
任务缓存:如果当前线程池中的线程数量未达到核心线程数(对于FixedThreadPool和CachedThreadPool),则立即创建新线程执行任务;如果达到核心线程数,则将任务放入工作队列中等待执行。
任务执行:线程池中的线程会不断从工作队列中取出任务并执行。
线程复用:当任务执行完毕后,线程不会立即销毁,而是会回到线程池中等待下一个任务。
线程销毁:如果线程池中的线程数量超过了核心线程数,并且这些线程在指定时间内没有执行任务,则这些线程会被销毁。
任务执行流程
-
提交任务:调用
execute(Runnable command)
方法提交任务 -
核心线程处理:
-
如果当前线程数 < corePoolSize,创建新线程执行任务
-
否则,尝试将任务加入工作队列
-
-
队列处理:
-
如果队列未满,任务入队等待执行
-
如果队列已满,尝试创建新线程(不超过maximumPoolSize)
-
-
拒绝策略:
-
如果线程数已达maximumPoolSize且队列已满,触发拒绝策略
-
线程生命周期
-
创建:当有新任务提交且当前线程数小于corePoolSize时创建
-
执行:从队列获取任务并执行
-
回收:
-
核心线程:默认不会回收,除非设置allowCoreThreadTimeOut
-
非核心线程:空闲超过keepAliveTime后被回收
-
我们先看看这些参数是什么意思:
-
corePoolSize: 核心线程数,即使空闲也会保留的线程数量
-
maximumPoolSize: 最大线程数,允许创建的最大线程数量
-
keepAliveTime: 非核心线程的空闲存活时间
-
workQueue: 任务队列
-
threadFactory: 线程工厂
-
handler: 拒绝策略
int corePoolSize:该线程池中核心线程数最大值
核心线程:线程池中有两类线程,核心线程和非核心线程。核心线程默认情况下会一直存在于线程池中,即使这个核心线程什么都不干(铁饭碗),而非核心线程如果长时间的闲置,就会被销毁(临时工)。
int maximumPoolSize:该线程池中线程总数最大值 。
该值等于核心线程数量 + 非核心线程数量。
long keepAliveTime:非核心线程闲置超时时长。
非核心线程如果处于闲置状态超过该值,就会被销毁。如果设置allowCoreThreadTimeOut(true),则会也作用于核心线程。
TimeUnit unit:keepAliveTime的单位。
TimeUnit是一个枚举类型。
BlockingQueue workQueue:阻塞队列,维护着等待执行的Runnable任务对象。
常用的几个阻塞队列:
LinkedBlockingQueue:链式阻塞队列,底层数据结构是链表,默认大小是Integer.MAX_VALUE,也可以指定大小。
ArrayBlockingQueue:数组阻塞队列,底层数据结构是数组,需要指定队列的大小。
SynchronousQueue:同步队列,内部容量为0,每个put操作必须等待一个take操作,反之亦然。
DelayQueue:延迟队列,该队列中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素 。
ThreadFactory threadFactory
创建线程的工厂 ,用于批量创建线程,统一在创建线程时设置一些参数,如是否守护线程、线程的优先级等。如果不指定,会新建一个默认的线程工厂。
RejectedExecutionHandler handler
拒绝处理策略,线程数量大于最大线程数就会采用拒绝处理策略,四种拒绝处理的策略为 :
ThreadPoolExecutor.AbortPolicy:默认拒绝处理策略,丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:丢弃新来的任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列头部(最旧的)的任务,然后重新尝试执行程序(如果再次失败,重复此过程)。
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。