在之前的文章中总结了Java线程的两种创建方式:继承Thread和实现Runnable接口,在Java中使用多线程不一定非得用此两种方式,JDK为我们封装了大量的线程实用类,本文主要对Java中的线程池ThreadPoolExecutor做一下详细的介绍。
线程池的好处
使用线程池比通过Thread或者Runnable直接实现多线程有哪些好处呢?
1.减少系统资源消耗:线程池中线程能够被复用,不需要每次创建一个线程执行任务,执行完任务之后进行线程销毁,减少线程创建与线程销毁带来的系统开销以及时间片轮转和上下文切换带来的开销;
2.提高执行任务的响应速度:将任务提交到线程池中,一旦线程池中有空闲线程,可以直接调用线程执行任务,节省线程创建所带来的时间;
3.线程的统一调度管理:能够对线程池中的线程都进行统一的管理、调度以及销毁;
ThreadPoolExecutor
JDK提供了ThreadPoolExecutor线程池,ExecutorService的各种线程池策略都是基于ThreadPoolExecutor实现的,因此有必要弄清楚ThreadPoolExecutor;当我们想线程池提交一个任务时,需要经过什么样的步骤?有什么不同的处理结果呢?
线程池组成部分:线程(核心线程与非核心线程)、任务队列(存储处于等待的任务)、线程工厂(用于创建线程)、异常处理handler、非核心线程空闲超时时间等部分组成;
线程池的状态:
1.RUNNING:接受新的任务,执行任务队列里面的任务;
2.SHUDOWN:不再接受新的任务,但会执行任务队列里面的任务;
3.STOP:不再接受新的任务,也不执行任务队列里面的任务;同时中断正在执行的任务;
4.TIDYING:所有的任务已经完成,工作线程为0;TIDYING会调用terminated()进入TERMINATED状态;
5.TERMINATED:terminated()函数执行完成,线程池终止;
线程池状态间的转换:
RUNNING -> SHUTDOWN:调用shutdown()函数
RUNNING -> STOP:调用shutdownNow()函数
SHUTDOWN -> STOP:调用shutdownNow()函数
SHUTDOWN -> TIDYING:任务队列和工作线程都为空(等待任务队列里面的任务执行完成,工作线程终止)
STOP -> TIDYING:工作线程都为空(当进入STOP状态后中断所有工作线程进入TIDYING)
TIDYING -> TERMINATED:调用terminated()函数执行完成(进入TIDYING状态会调用terminated()函数)
往线程池中提交一个任务,要经过一下步骤:
步骤1:如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务,而不会把这个任务添加到任务队列中;
步骤2:如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务插入任务队列中,不会马上执行该任务,等待运行的线程执行完当前的任务空闲后,再从任务队列中取出任务继续执行。
步骤3:如果这时候任务队列满了,无法继续插入到任务队列,如果此时正在运行的线程数量小于 maximumPoolSize,那么还是会创建一个线程来运行这个任务;
步骤4:如果任务队列满了,并且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”,通过异常处理handler进行处理。
当然上述步骤不是完全的步骤,当在执行过程中发现线程池的状态为非RUNNING状态时,则抛出异常,拒绝此任务,交给异常处理handler进行处理;
任务执行策略
根据上面的介绍我们可以知道,当线程池正在运行的线程大于或者等于corePoolSize时,任务队列的大小直接决定任务的执行;任务队列的大小可以分为三种:size为0的队列、有界队列以及无界队列;
1.size为0的队列
任务队列大小为0,每次往任务队列中插任务时都失败,此时正在运行的线程小于maximumPoolSize时,则会创建新的线程直接运行任务,或者由空闲线程执行任务;否则会拒绝接受此任务,交给异常处理handler进行处理。
线程池的特点
1.线程池线程数量最多为maximumPoolSize个,最少为0个
2.提交线程池的任务不需要等待,当前线程小于corePoolSize时,直接创建新的线程执行;如果当前运行的线程数量等于或者大于maximumPoolSize时,则直接拒绝接受此任务,交给异常处理handler进行处理;
3.线程池最多同时执行maximumPoolSize个任务
下面我们来看一个例子
创建一个任务队列大小为0的ThreadPoolExecutor,其中corePoolSize为1,maximumPoolSize为2
public static ExecutorService synchronousQueueExecutor() {
return new ThreadPoolExecutor(1, 2,
60L, TimeUnit.SECONDS,