目录
为什么引入Executor线程池框架
① 重用存在的线程,减少对象创建、消亡的开销,提高性能;
② 线程的创建和运行分开,达到解耦目的;
③ 可有效控制最大并发线程数,提高系统资源的使用率;
Executor原理
Executor生命周期
- RUNNING:可以接收新任务,并且处理阻塞队列中的任务
- SHUTDOWN:关闭状态,不能接收新任务,可以继续处理阻塞队列中的任务。在线程池处于 RUNNING 状态时,调用 shutdown()方法会使线程池进入到该状态。
- STOP:不能接收新任务,也不能处理阻塞队列中的任务,会中断正在处理的任务。在线程池处于 RUNNING 或 SHUTDOWN 状态时,调用 shutdownNow() 方法会使线程池进入到该状态。
- TIDYING:所有任务都执行完成,线程池中workerCount (有效线程数) 为0。线程池进入该状态后会调用 terminated() 方法进入TERMINATED 状态。
- TERMINATED:调用terminated()方法进入该状态。
Executor框架介绍与使用
说明:
- Executor 执行器接口,该接口定义执行Runnable任务的方式。
- ExecutorService 该接口定义提供对Executor的服务。
- ScheduledExecutorService 定时调度接口。
- AbstractExecutorService 执行框架抽象类。
- ThreadPoolExecutor JDK中线程池的具体实现。
- Executors 线程池工厂类。
Executor将任务的提交过程与执行过程解耦,并用Runnable来表示任务。执行的任务放入run方法中即可,将Runnable接口的实现类交给线程池的execute方法,作为它的一个参数。如果需要给任务传递参数,可以通过创建一个Runnable接口的实现类来完成。
Executor可以支持多种不同类型的任务执行策略。
Executor基于生产者消费者模式,提交任务的操作相当于生产者,执行任务的线程则相当于消费者。
ThreadPoolExecutor 线程池类
线程池是一个复杂的任务调度工具,它涉及到任务、线程池等的生命周期问题。要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的。
JDK中的线程池均由ThreadPoolExecutor类实现。其构造方法如下:
corePoolSize(线程池的基本大小):当提交一个新的任务给线程池后,会创建一个新的线程来执行任务(当前线程池线程数 < corePoolSize),等到需要执行的任务数大于线程池基本大小大于corePoolSize时就不再创建,而是把任务放进保持的等待队列
maximumPoolSize(线程池最大大小):线程池允许最大线程数。如果阻塞队列满了,并且已经创建的线程数小于最大线程数,则线程池会再创建新的线程执行。因为线程池执行任务时是线程池基本大小满了,后续任务进入阻塞队列,阻塞队列满了,在创建线程。
keepAliveTime(线程活动保持时间):空闲的线程保持活动得时间(好像只有在newCachedThreadPool这个方法的参数中才有意义(不为0))
TimeUnit(线程活动保持时间的单位):
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
workQueue(工作队列):用于保存待执行的任务的阻塞队列,有如下几种:
- ArrayBlockingQueue:数组结构的有界阻塞队列,先进先出FIFO
- LinkedBlockingQueue:链表结构的无界阻塞队列。先进先出FIFO排序元素,静态方法Executors.newFixedThreadPool和Executors.newFixedThreadPool.newSingleThreadExecutor使用这个队列
- SynchronousQueue:不存储元素的阻塞队列,就是每次插入操作必须等到另一个线程调用移除操作,静态方法Executors.newCachedThreadPool使用这个方法
threadFactory:设置创建线程的工厂,通过这个工厂可以给线程一些比较有意义的名字
handler:一种饱和策略,当线程池和队列都饱满时拒绝处理任务的策略,默认是AbortPolicy,表示无法处理新的任务时抛出异常;拒绝策略有如下几种:
-
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
-
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
-
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
-
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
ThreadPoolExecutor需要注意以下概念:
-
若线程池中的线程数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
-
若线程池中的线程数量等于 corePoolSize且缓冲队列 workQueue未满,则任务被放入缓冲队列。
-
若线程池中线程的数量大于corePoolSize且缓冲队列workQueue满,且线程池中的数量小于maximumPoolSize,则建新的线程来处理被添加的任务。
-
若线程池中线程的数量大于corePoolSize且缓冲队列workQueue满,且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
- 当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。
ExecutorService接口
线程池接口。ExecutorService在Executor的基础上增加了一些方法,其中有两个核心的方法:
Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)
这两个方法都是向线程池中提交任务,它们的区别在于Runnable在执行完毕后没有结果,Callable执行完毕后有一个结果。这在多个线程中传递状态和结果是非常有用的。另外他们的相同点在于都返回一个Future对象。Future对象可以阻塞线程直到运行完毕(获取结果,如果有的话),也可以取消任务执行,当然也能够检测任务是否被取消或者是否执行完毕。
在没有Future之前我们检测一个线程是否执行完毕通常使用Thread.join()或者用一个死循环加状态位来描述线程执行完毕。现在有了更好的方法能够阻塞线程,检测任务执行完毕甚至取消执行中或者未开始执行的任务。
Executor接口
Executor是一个线程执行接口。任务执行的主要抽象不是Thead,而是Executor。
public interface Executor{
void executor(Runnable command);
}
Executor将任务的提交过程与执行过程解耦,并用Runnable来表示任务。执行的任务放入run方法中即可,将Runnable接口的实现类交给线程池的execute方法,作为它的一个参数。如果需要给任务传递参数,可以通过创建一个Runnable接口的实现类来完成。
Executor可以支持多种不同类型的任务执行策略。
Executor基于生产者消费者模式,提交任务的操作相当于生产者,执行任务的线程则相当于消费者。
ScheduledExecutorService接口
ScheduledExecutorService描述的功能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。这包括延迟时间一次性执行、延迟时间周期性执行以及固定延迟时间周期性执行等。当然了继承ExecutorService的ScheduledExecutorService拥有ExecutorService的全部特性。
①:Java类库可以通过调用Executors的静态工厂方法来创建线程
public class Executors extends Object
static ExecutorService | newCachedThreadPool() 创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。 |
static ExecutorService | newCachedThreadPool(ThreadFactory threadFactory) 创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程,并在需要时使用提供的ThreadFactory创建新线程。 |
static ExecutorService | newFixedThreadPool(int nThreads) 创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。 |
static ExecutorService | newFixedThreadPool(int nThreads, ThreadFactory threadFactory) 创建一个线程池,重用固定数量的线程,从共享无界队列中运行,使用提供的ThreadFactory在需要时创建新线程。 |
static ScheduledExecutorService | newScheduledThreadPool(int corePoolSize) 创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行。 |
static ScheduledExecutorService | newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) 创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行。 |
②:public interface ExecutorService extends Executor;
boolean | isShutdown() 如果此执行者已关闭,则返回 |
boolean | isTerminated() 如果所有任务在关闭后完成,则返回 |
void | shutdown() 启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务。 |
List<Runnable> | shutdownNow() 尝试停止所有主动执行的任务,停止等待任务的处理,并返回正在等待执行的任务列表。 |
<T> Future<T> | submit(Callable<T> task) 提交值返回任务以执行,并返回代表任务待处理结果的Future。 |
public static void main(String[] args) {
//固定长度的线程池
//ExecutorService pool = Executors.newFixedThreadPool(5);
//将创建一个缓存线程池
ExecutorService pool = Executors.newCachedThreadPool();
//可调度的线程池
//ExecutorService pool = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 10; i++) {
pool.execute(new ThreadDemo1());
}
//停止
pool.shutdown();
}