线程池是一种线程使用模式,为了应对频繁的创建和销毁线程会带来调度开销,影响缓存局部性和整体性能而创建。线程池内可以维护多个线程,不需要频繁和销毁,而是统一进行管理,只需要管理者分配可并发执行的任务即可,避免了在处理短时间任务时创建与销毁线程的代价。
线程池的原理和数据库连接池是类似的,为了能够复用线程、限制并发数以及减少上下文切换带来的资源消耗,使用池化的技术对线程进行管理。
线程池是事先创建若干个可执行的线程放入一个池中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。线程池做的工作主要是控制运行的线程的数量,处理过程中将任务加入队列,然后在线程创建后启动这些任务,如果线程数超过了最大数量,超出的数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。
jdk给我们提供了Executor框架、Executors工具类,来创建和使用线程池,架构图如下:
1. 通过 ThreadPoolExecutor 创建的线程池
2. 通过 Executors 创建的线程池
Executor接口:只有一个待实现的方法,用来执行一项Runnable任务
ExecutorService接口:对Executor进行了扩展,提供了更多的方法,一般使用它来代替Executor
ThreadPoolExecutor:Executor框架的正真实现者,实现了execute方法
private final HashSet<Worker> workers = new HashSet<>(); //Worker集合,线程池
private final BlockingQueue<Runnable> workQueue; //阻塞队列,要执行的任务
final void runWorker(Worker w); //Worker调用这个方法,可以从阻塞队列中获取任务来执行。
其构造函数接收的参数:
1、public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue) {}
2、public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory) {}
3、public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) {}
4、public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,
RejectedExecutionHandler handler) {}
corePoolSize:核心线程数,也即最小的线程数,线程池会维护一个最小的线程数。
maximumPoolSize:最大的线程数
keepAliveTime:线程的存活时间。默认情况下,只有当线程池中的线程数大于corePoolSize时,才会起作用,直到线程池中的线程数不大于corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
unit:存活时间的单位
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
workQueue:存放任务的阻塞队列,使用阻塞队列可以管理线程的组合和唤醒。
ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于数组的
SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQuene;
priorityBlockingQuene:具有优先级的无界阻塞队列;
threadFactory:创建线程的线程工厂
Executors.defaultThreadFactory():工具类的默认工厂
handler:线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
new ThreadPoolExecutor.AbortPolicy(默认,中止策略):丢弃任务,抛出
RejectedExecutionException异常阻止系统正常运行。
new ThreadPoolExecutor.CallerRunsPolicy(回退策略):将提交的任务会退给调用线程,让调用线程执行。
new ThreadPoolExecutor.DiscardOldestPolicy(抛弃最老策略):丢弃队列最前面的任务,并将新任务加入
new ThreadPoolExecutor.DiscardPolicy(抛弃策略):丢弃任务,但是不抛出异常。
线程池监控API:
- 如果线程池中的线程数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
- 如果线程池中的线程数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。
- 如果线程池中的线程数量大于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
- 如果线程池中的线程数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过handlerf所指定的策略来处理此任务。
- 当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数
Executors它是一个线程工具类,内部封装的方法能帮助我们创建线程池:
**注意:**阿里的开发手册中是禁止使用Executors的
虽然Exectors工具类给我们提供了一些方法,可以方便的获取一些配置好的ThreadPoolExecutor类,但是它们的配置有一些不合理的地方,在实际生产中,我们一般会自定义线程池,而不是使用jdk提供的。以下是阿里中的规定:
【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors返回的线程池对象的弊端如下: 1)FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAx_vALUE,可能会堆积大量的请求,从而导致ooM。 2)CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为snteger.MAx_vALUE,可能会创建大量的线程,从而导致ooM。
- 1
- 2
- 3
- 4
创建固定容量的线程池
创建单个线程的线程池
容量可变的线程池
Executors工具类创建的线程池,本质上也是构建ThreadPoolExecutor类,所以我们也可以直接根据ThreadPoolExecutor类的四个构造方法去创建线程。
public ThreadPoolExecutor(int corePoolSize, //最小线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //线程没有任务执行时最多保持多久时间会终止。
TimeUnit unit, //存活时间
BlockingQueue<Runnable> workQueue) {} //存放任务的阻塞队列
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {} //线程工厂
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {} //线程池的饱和策略
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {}