目录
什么是线程池
个人理解: 线程池就是管理并维护了若干线程,并对这些线程进行分配调度以及一些别的操作,是一套具有完整执行流程的机制。
线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待空闲状态。如果有新的线程任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,线程池会创建一个新线程进行处理或者放入队列(工作队列)中等待。
线程池的优点
- 线程池应用了池化的思想,主要为了减少每次获取资源的消耗,提高对资源的利用率,提供了一种限制、管理资源的策略。每个线程池还维护了一些基本统计信息,例如已完成任务的数量;
- 它对各个线程进行了统一管理,更加利于维护和调度;
- 降低资源消耗: 通过重复利用已创建的线程降低线程创建和销毁造成的消耗;
- 提高响应速度: 当任务到达时,可以不需要等待线程创建就能立即执行(在线程池中有空闲线程时);
- 提高线程的可管理性: 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,监控和调优。
线程池的执行流程
- 提交一个线程任务,线程池会分配一个空闲线程,用于执行该任务;
- 如果线程池中目前没有空闲线程,则线程池会判断当前"存活线程数"是否小于核心线程数corePoolSize:
- 如果小于核心线程数,那么会创建一个新的线程(核心线程)去处理新线程任务;
- 如果大于核心线程数corePoolSize,线程池会检查工作队列:
- 如果工作队列未满,则将该线程任务放入工作队列进行等待。线程池中如果出现空闲线程,则会将工作队列里的任务按照FIFO的原则取出1个并分配执行;
- 如果工作队列已满,则判断当前线程数是否达到最大线程数maximumPoolSize:
- 如果当前"存活线程数"没有达到最大线程数,则创建一个新线程(非核心线程)去执行新线程任务;
- 如果当前"存活线程数"达到最大线程数maximumPoolSize,直接采用拒绝策略处理新线程任务。
总的执行流程就是: 核心线程=>工作队列=>非核心线程=>拒绝策略。
线程池的配置参数
1. corePoolSize核心线程数: 可以理解为线程池维护的最小线程数量。核心线程创建后不会被回收。大于核心线程数的线程,在空闲时间超出keepAliveTime后会被回收;
- 在创建了线程池后,默认情况下,线程池中并没有任何线程,当调用execute()方法添加一个线程任务时,如果正在运行的线程数量小于corePoolSize,则会创建新的核心线程去执行这个任务。
- IO密集计算:由于 I/O 设备的速度相对于 CPU来说都很慢,所以大部分情况下,I/O 操作执行的时间相对于 CPU 计算来说都非常长,这种场景我们一般都称为 I/O 密集型计算。最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]。
- CPU密集型:CPU 密集型计算大部分场景下都是纯 CPU 计算,多线程主要目的是提升CPU利用率,最佳线程数 =“CPU 核心数 +1”。这样的话,当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程可以临时替补,从而保证 CPU 的利用率。
2. maximumPoolSize最大线程数: 线程池允许创建的最大线程数量;(包括核心线程数)
3. keepAliveTime 非核心线程的存活时间: 当一个非核心线程的空闲时间大于keepAliveTime时,就会被回收;
- 当线程池中的线程数大于corePoolSize时,如果一个非核心线程空闲的时间达到keepAliveTime,则会被回收,直到线程池中的线程数不超过corePoolSize。
- 如果设置allowCoreThreadTimeOut = true,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
4. TimeUnit时间单位:参数keepAliveTime的时间单位;
5. BlockingQueue阻塞工作队列:用来存储等待执行的任务;
6. ThreadFactory线程工厂 : 用于创建线程,以及自定义线程名称,需要实现ThreadFactory接口;
7. RejectedExecutionHandler拒绝策略:当线程池线程内的线程耗尽,并且工作队列达到已满时,新提交的任务,将使用拒绝策略进行处理;
- ThreadPoolExecutor.AbortPolicy:默认策略,丢弃任务并抛出RejectedExecutionException异常;
- ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常;
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃工作队列中的队头任务(即最旧的任务,也就是最早进入队列的任务)后,继续将当前任务提交给线程池;
- ThreadPoolExecutor.CallerRunsPolicy:由原调用线程处理该任务 (谁调用,谁处理);
线程池的状态
线程池的状态分为: RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED。
RUNNING: 运行状态,线程池一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0。该状态的线程池会接受新任务,并处理工作队列中的任务。
- 调用线程池的shutdown()方法,可以切换到SHUTDOWN关闭状态;
- 调用shutdownNow()方法,可以切换到STOP停止状态。
SHUTDOWN: 关闭状态,该状态的线程池不会接收新任务,但会处理工作队列中的任务;
- 当工作队列为空时,并且线程池中执行的任务也为空时,线程池进入TIDYING状态;
STOP: 停止状态,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
- 线程池中执行的任务为空,进入TIDYING状态;
TIDYING:整理状态,该状态表明所有的任务已经运行终止,记录的任务数量为0;
- terminated()执行完毕,进入TERMINATED状态;
TERMINATED: 终止状态,该状态表示线程池彻底关闭。