线程池
线程池
线程池接口
ExecutorService
访问操作线程池,继承Executor接口。
ThreadPoolExecutor及其子类
线程池的具体实现,实现了AbstractThreadPoolExecutor抽象类。
Executors工具类 (封装了常用线程池的创建)
根据设定参数的不同,可以分为下面四种常用的线程池对象:
动态数目线程池对象(ThreadPoolExecutor)
线程在存活时间内(60秒)可以重复使用
Executors.newCachedThreadPool();
固定数目线程池对象(ThreadPoolExecutor)
Executors.newFixedThreadPool(20);
单个线程池对象(ThreadPoolExecutor)
Executors.newSingleThreadExecutor();
任务调度线程池对象(ScheduledThreadPoolExecutor)
Executors.newScheduledThreadPool(20);
提交调度线程任务
- schedule() 指定延迟时间后,执行线程任务1次
- scheduleAtFixedRate() 固定的时间间隔,触发执行线程任务
- scheduleWithFixedDelay() 上一次任务执行结束后,"延迟"时间后执行
线程池参数
1、corePoolSize:核心线程数,线程池中最少线程,核心线程不会被回收。
2、maximumPoolSize:最大线程数,线程池中最多线程,包含核心线程数,不能小于核心线程数。
3、keepAliveTime:非核心线程(除去核心线程之外的线程)存活时间,如果非核心线程的空闲时间大于此参数,将会被回收。
4、TimeUnit:时间单位,参数keepAliveTime的时间单位。
5、BlockingQueue:阻塞工作队列,当来一个新的线程任务时,如果当前没有空闲线程,此线程任务将会进入阻塞工作队列中进行等待。
6、ThreadFactory:线程工厂,用于创建线程,自定义线程的名称。(需要实现ThreadFactory接口,实现newThread()方法,在此方法中,新建Thread对象,通过调用setName()方法给线程定义名称。)
class MyThreadFactory implements ThreadFactory{
public AtomicInteger threadNumber = new AtomicInteger();
public String preFix;
public MyThreadFactory(String preFix) {
this.preFix = preFix;
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r,preFix + threadNumber.getAndIncrement());
if(t.isDaemon()) {
t.setDaemon(false);
}
if(t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}
7、RejectedExecutionHandler:拒绝策略,当线程池中没有空闲线程,且阻塞工作队列已满,且最大线程数也已超出,此时再来线程任务将执行拒绝策略。
四种拒绝策略
AbortPolicy :丢弃当前线程任务,并抛出异常
DiscardPolicy :丢弃当前线程任务,但不抛出异常
DiscardOldestPolicy : 丢弃工作队列中最“老”的线程任务
CallerRunsPolicy :直接由“当前调用线程”直接执行该线程任务
自定义拒绝策略 :实现RejectedExecutionHandler接口
线程池的执行流程
首先,通过execute()或者submit()提交线程任务到线程池中,然后由线程池判断是否可以执行,反之执行拒绝策略。
如上图所示,就是线程池的执行过程,可以分为五个主要步骤:
1.提交任务后会首先进行判断是否存在空闲线程,如果存在直接执行线程任务。
2.若果当前工作线程数与核心线程数的比较,如果当前工作线程数小于核心线程数,则直接调用 addWorker() 方法创建一个核心线程去执行任务;
3.如果工作线程数大于核心线程数,即线程池核心线程数已满,则新任务会被添加到阻塞队列中等待执行,当然,添加队列之前也会进行队列是否为空的判断;
4.如果线程池里面存活的线程数已经等于核心线程数了,且阻塞队列已经满了,再会去判断当前线程数是否已经达到最大线程数 maximumPoolSize,如果没有达到,则会调用 addWorker() 方法创建一个非核心线程去执行任务;
5.如果当前线程的数量已经达到了最大线程数时,当有新的任务提交过来时,会执行拒绝策略
总结来说就是优先核心线程、阻塞队列次之,最后非核心线程。
线程池的状态
RUNNING 运行状态
允许新任务,并处理工作队列中的任务
- 调用shutdown()方法 ,进入关闭状态
- 调用shutdownNow() /stop()方法,进入停止状态
SHUTDOWN 关闭状态
不接收新任务,但仍处理工作队列中的任务
STOP 停止状态
不接收新任务,也不处理工作队列中的任务,同时中断正在执行的任务
TIDYING 整理状态
所有任务已经结束,工作队列中的任务数量为0
TERMINATED 终止状态
线程池彻底关闭
线程池如何保存状态和线程数量
AtomicInteger ctl 变量 ,前3位保存“线程状态”,后29位保存“线程数量(线程池存活线程数)”