要了解线程池的执行流程,我们首先就要知道什么是线程池?
线程池就是里面含有若干线程的容器,没有任务时,线程池里面的这些线程都处于等待空闲状态。如果有新的线程任务,就分配一个空闲线程执行任务。如果所有的线程都处于忙碌状态,线程池就会创建一个新的线程,亦或者是将任务放到工作队列中等待。
一、线程池的执行流程
线程池的执行流程基本是:核心线程、工作队列、非核心线程、拒绝策略
-
当出现一个新的线程任务时,线程池会先在线程池中分配一个空闲线程,来执行这个任务
-
如果出现新任务时线程池中没有空闲线程,这时线程池就会判断当前“存活线程数”是否小于核心线程数corePoolSize
-
“存活线程数” < corePoolSize: 线程池就会创建一个新的线程,用来处理新任务
-
“存活线程数” = corePoolSize: 这时线程池就要判断工作队列了(队列中存储的就是等待执行的线程)
- 工作队列未满(即还有位置可以存放任务):就将该线程放入工作队列中进行等待,等线程池中出现空闲线程,就按照“先进先出”的规则分配执行
- 工作队列已经满了:判断当前存活线程数是否已经达到最大线程数
- “存活线程数” < “最大线程数”:创建一个新线程执行新任务
- “存活线程数” > “最大线程数”:直接采用拒绝策略
-
二、线程池的操作与使用
Java中提供了三种类或接口来使用线程池,分别是:
1、ExecutorService接口:进行线程池的操作访问
2、Executors类:创建线程池的工具类
3、ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制
//线程池基本使用方式
//创建一个ThreadPoolExecutor类型的对象,代表固定大小的线程池
ExecutorService executorService=Executors.newFixedThreadPool(4);//该线程拥有四个线程
//执行任务
executorService.execute(task1);
executorService.execute(task2);
executorService.execute(task3);
executorService.execute(task4);
executorService.execute(task5);
//使用结束后关闭线程
executorService.shutdown();
线程池的创建有四个参数分别是:
corePoolsize线程池核心线程数
maximumPoolSize线程池最大线程数
keepAliveTime非核心线程存活时间
BlockingQueue阻塞工作队列
三、执行线程任务
execute():只能提交Runnable类型的任务,没有返回值;当任务出现异常时通常直接抛出
submit():既能提交Runnable类型任务也能提交Callable类型任务,可以返回Future类型结果,用于获取线程任务执行结果;当任务产生异常时通常捕获异常
public class Pool {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 计算1-100w的之间所有数字的累加和,每10w个数字交给1个线程处理
// 创建一个固定大小的线程池
ExecutorService executorService=Executors.newFixedThreadPool(4);
// 创建集合,用于保存Future执行结果
List<Future<Integer>> futureList=new ArrayList<Future<Integer>>();
// 每10w个数字,封装成一个Callable线程任务,并提交给线程池
for(int i=0;i<=900000;i+=100000) {
Future<Integer> result=executorService.submit(new CalcTask(i+1, i+100000));
futureList.add(result);
}
int result=0;
for(int i=0;i<futureList.size();i++) {
result+=futureList.get(i).get();
}
System.out.println("最终计算结果:"+result);
executorService.shutdown();
// 每隔1秒钟,检查一次线程池的任务执行状态
while(!executorService.awaitTermination(1, TimeUnit.SECONDS)) {
System.out.println("还没有关闭");
}
System.out.println("线程池关闭");
}
}
//计算任务
class CalcTask implements Callable<Integer>{
private int begin,end;
public CalcTask(int begin, int end) {
super();
this.begin = begin;
this.end = end;
}
@Override
public Integer call() throws Exception {
int result=0;
for(int i=begin;i<=end;i++) {
result+=i;
}
System.out.printf("线程%s计算%d-%d范围的任务结束!\n",Thread.currentThread().getName(),begin,end);
return result;
}
}
四、关闭线程池
关闭线程池有两种方法:
- shutdown()方法:此方法在当前任务执行完毕后关闭线程池
- shutdownNow()方法:此方法会离开执行关闭线程池操作,无论当前正在执行的任务有没有执行完
awaitTermination()方法:可以检查线程池是否关闭
五、线程池的状态
线程池的状态可分为: RUNNING, SHUTDOWN, STOP, TIDYING , TERMINATED
RUNNING:运行状态:线程池一旦被创建就处于RUNNING状态
SHUTDOWN :关闭状态:该状态线程池不会接收新任务,但会处理工作队列中的任务
STOP:停止状态:该状态不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务
TIDYING:整理状态:所有任务已运行终止
TERMINATED:终止状态:该线程池彻底关闭