1.概述:线程池维护了一组线程,避免了多个线程重复的创建与销毁而造成资源的浪费,线程池技术主要由2个核心接口和一组实现类组成。
2.线程池相关接口和实现类
Executor接口是线程池技术中的顶层接口,它的作用是用来定义线程池中,用来提交并执行线程任务的核心方法:execute()方法。未来线程池中所有的线程任务,都将由execute()方法来执行。
ExecutorService接口继承了Executor接口,扩展了awaitTermination()、submit()、shutdown()等专门用于管理线程任务的方法。
ExecutorService接口的抽象实现类AbstractExecutorService,为不同的线程池实现类,提供submit()、invokeAll()方法等部分方法的公共实现。但是不同的线程池中的execute()方法执行策略不同,所以AbstractExecutorService没有对execute()方法进行具体的实现。
AbstractExecutorService的子类常见的有ThreadPoolExecutor和ForkJoinPool,用于实现不同的线程池。
ThreadPoolExecutor类通过Work工作线程。BlockingQueue阻塞工作队列以及拒绝策略实现一个标准的线程池。
ForkJoinPool是一个基于分治思想的线程池实现类,通过分叉合并的方式,将一个大任务拆分成多个小任务,并且为每个工作线程提供一个工作队列,减少竞争,实现并行的线程任务执行方式,所以ForkJoinPool适合计算密集型场景,是ThreadPoolExecutor线程池的一种补充。
3.核心方法execute()
ThreadPoolExecutor线程池中,会通过execute(Runnable command)方法执行Runnable类型的线程任务。
execute()方法:
//线程池执行Runnable类型的线程任务
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//获取线程池的工作状态和工作线程数
// 线程池用一个32位的整数代表工作状态和工作线程数,前3位代表工作状态,后29位代表工作线程数
int c = ctl.get();
//如果线程池的工作线程数小于核心线程数
if (workerCountOf(c) < corePoolSize) {
//调用addWorker()方法创建新的工作线程执行线程任务
if (addWorker(command, true))
return;
//创建失败,重新获取线程池的工作状态和工作线程数
c = ctl.get();
}
//如果线程池是RUNNING状态,则将线程任务添加到工作队列中
if (isRunning(c) && workQueue.offer(command)) {
//添加成功,重新获取线程池的工作状态和工作线程数
int recheck = ctl.get();
//如果线程池不是RUNNING状态,则删除线程任务
if (! isRunning(recheck) && remove(command))
//执行拒绝策略
reject(command);
//如果线程池的工作线程数为0,调用addWorker()方法检查线程池状态和工作队列
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//如果线程任务添加到工作队列中,尝试创建新的工作线程,如果创建失败,则执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
execute()方法的执行流程如下:
1.首先获取线程池的工作状态和工作线程数(线程池用一个32位的整数代表工作状态和工作线程数,前3为代表工作状态,后29位代表工作线程数) 。
2.判断当前线程数是否小于核心线程数,如果小于,调用addWorker()方法创建一个新的工作线程并添加至工作线程集合workers中。
3.如果当前线程数大于核心线程数并且线程池为RUNNING状态,将线程任务添加到workQueue工作队列中,等待某个空闲工作线程获取并执行任务。
4.如果workQueue添加线程任务失败了,代表工作队列已满,线程池就会调用addWorker()方法创建新的Worker工作线程。
5.这次在创建新的Worker工作线程时,会判断当前线程池的线程数是否超过最大线程数,如果超过,则返回false,代表创建失败,执行拒绝策略。