线程池是什么
创建一定数量的可执行线程放入一个容器(池)中,这个池就叫做线程池,我们也可以把线程池理解成是一组线程的集合。
为什么要用线程池
首先我们要知道,线程是很宝贵的资源,因为在Java中创建线程需要调用操作系统内核的Api,从而使操作系统为线程分配所需的资源,成本很高。那么我们就应该尽量的避免这样的宝贵资源重复创建和销毁,而使用线程池就能够做到。因为线程池中的线程在执行完任务后,会再次回归到线程池中,等待下次任务的执行,并不会被销毁,做到了线程的复用。
线程池的核心组件
- 线程池管理器:用于线程池的创建和管理
- 工作线程:线程池中用来执行具体任务的线程
- 任务接口:用于定义工作线程的调度和执行策略,线程只有实现了这个接口,线程中的任务才能够被线程池调度,任务接口主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
- 任务队列:用于存放待处理的任务,起到了缓冲的效果,每当有新的任务就会被加入这个队列,执行完成的任务就会被移除出队列。
线程池的核心类
- Executor
- Executors
- ExecutorService
- ThreadPoolExecutor
- Future
- FutureTask
- Callable
类图如下:
构建线程池的核心方法参数作用
ThreadPoolExecutor类是构建线程池的核心类,构造方法如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
参数 | 作用 |
---|---|
corePoolSize | 线程池中的核心线程数 |
maximumPoolSize | 线程池中的最大线程数 |
keepAliveTime | 当线程数量超过corePoolSize时,空闲线程的存活时间 |
unit | keepAliveTime的时间单位 |
workQueue | 任务队列,存放待处理的任务 |
Executors.defaultThreadFactory() | 线程工厂,用于创建线程 |
defaultHandler | 因为一些原因导致任务无法被处理时的拒绝策略 |
线程池的工作流程
从上面的流程图中可以看出来,如果是无界任务队列(大小无限制)的话,那么就不会往最大线程数方向创建,同时只有任务队列满了,才会走创建最大线程数这个逻辑,最后等到最大线程数也满了,就会执行拒绝策略。
线程池的拒绝策略
-
AbortPolicy:该拒绝策略直接抛出异常,阻止线程的正常运行
-
DiscardPolicy:丢弃当前的线程任务而不做任何处理
-
DiscardOldestPolicy:移除线程队列中最早的一个线程任务,同时尝试提交当前任务
-
CallerRunsPolicy:如果被丢弃的线程任务未关闭,则执行该线程任务,不过CallerRunsPolicy拒绝策略不会真的丢弃任务
-
自定义拒绝策略:自定义一个类,实现RejectedExecutionHandler类,根据自己的业务逻辑重写rejectedExecution()方法。
5种常用的线程池说明
名称 | 说明 |
---|---|
newCachedThreadPool | 可缓存的线程池 |
newFixedThreadPool | 固定大小的线程池 |
newSingleThreadExecutor | 固定单个线程的线程池 |
newScheduledThreadPool | 用作任务调度的线程池 |
newWorkStealingPool | 足够大小的线程池,JDK1.8新增的 |