线程池不会使用我们传入的实现了Runnable接口的类去当作一个线程直接启动,而是使用自己的线程去实现Runnable中的任务。
我们为什么要用线程池?
线程池基于池化技术来实现线程的创建与管理,减少了频繁创建销毁线程所用的时间和资源,加大了线程的复用,方便管理。
线程池的组成部分
java中的线程池由下列四个核心组件组成
1、线程池管理器
负责线程的创建、销毁、重用,并且可以控制线程的数量和运行状态,Java 中的线程池通常使用 ThreadPoolExecutor 类来实现线程池管理器。
2、任务队列
任务队列是线程池中的一个缓冲区,用于存储待执行的任务。线程池中创建的线程会从任务队列中取出任务并进行执行。
Java 中的线程池提供了两种类型的任务队列:有界队列和无界队列。有界队列可以限制任务队列的最大长度,控制待处理任务的数量。而无界队列则没有长度限制,可以不断向队列中添加新的任务。
3、线程工厂
线程工厂用于创建新线程,赋予其名称和优先级等属性,Java 中的线程工厂接口为 ThreadFactory
4、拒绝策略
拒绝策略是线程池中的一种保护机制,用于处理任务队列已满导致无法执行新任务的情况。当任务队列已满时,拒绝策略会将新任务直接拒绝或者采用其他方式处理,例如丢弃任务、阻塞任务或者抛出异常等。
Java 中的线程池提供了四种默认的拒绝策略:
- AbortPolicy:直接抛出异常,拒绝新任务的提交。
- CallerRunsPolicy:使用调用者所在的线程来执行任务。
- DiscardOldestPolicy:丢弃任务队列中最旧的未处理任务,并尝试重新提交新任务。
- DiscardPolicy:直接丢弃新的任务。
常见的线程池有哪些?
常见的线程池大概有以下几种:
- FixedThreadPool
- CachedThreadPool
- SingleThreadPool
- ScheduledThreadPool
- ThreadPoolExecutor
FixedThreadPool:
只设置线程池中线程数量的上限,而没有下限,在此线程池中,核心池的线程数量就是最大池的线程数量
CachedThreadPool
这种线程池是无界的,核心池大小为0,当主线程提交任务的速度快于线程中任务处理的速度时,会不断的创建新线程,极端情况下会创建过多的线程,耗尽 CPU 和内存资源,因此适合任务量大但处理速度快的场景。当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销。当一个空闲线程的空闲时间超过60s时,将销毁这个线程。
SingleThreadPool
这里面只有一个线程。它以保证所有的任务,按照你传入的顺序来执行。
ScheduledThreadPool
此线程池可以指定某些任务在延迟一段时间后执行。
ThreadPoolExecutor
由ThreadPoolExecutor类创建,可手动配置核心池、最大池等信息,更加灵活
ThreadPoolExecutor executor = new ThreadPoolExecutor(
20, // corePoolSize
100, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS, // unit
new LinkedBlockingQueue<Runnable>() // workQueue
);
线程池创建线程的全过程
线程池的五种状态
运行态、停止态、关闭态、整理态、终止态
RUNNING
线程池的初始化状态是RUNNING,此状态的线程池可以处理已添加的任务,也可以接收新任务。
SHUTDOWN
调用线程池的shutdown()时,线程池由运行态转化为关闭态,所谓关闭,就是不接收新任务,但还可以处理已添加的任务。
STOP
运行态或者关闭态可转化为停止态,此状态下不接收新任务也不处理已添加的任务,并且会中断正在处理的任务。
TIDYING
当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
当线程池在关闭状态下,任务队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在停止状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
TERMINATED
当线程池处于整理态时,执行完终止函数terminated()之后,就会由整理态转化为终止态,此状态意味着线程池彻底终止。