文章目录
Java并发——Executor框架详解
一、Executor框架概念
我们知道线程池就是线程的集合,线程池集中管理线程,以实现线程的重用,降低资源消耗,提高响应速度等。线程用于执行异步任务,单个的线程既是工作单元也是执行机制,从JDK1.5开始,为了把工作单元与执行机制分离开,Executor框架诞生了,他是一个用于统一创建与运行的接口。Executor框架实现的就是线程池的功能。
二、Executor框架结构详解
2.1 Executor框架
(1)任务。也就是工作单元,包括被执行任务需要实现的接口:Runnable接口或者Callable接口;
(2)任务的执行。也就是把任务分派给多个线程的执行机制,包括Executor接口及继承自Executor接口的ExecutorService接口。
(3)异步计算的结果。包括Future接口及实现了Future接口的FutureTask类。
Executor框架的成员及其关系可以用一下的关系图表示:
2.2 类与接口简介:
Executor是一个接口,它是Executor框架的基础,它将任务的提交与任务的执行分离开来
ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务
ScheduledThreadPoolExecutor是一个实现类,可以在给定的延迟后运行命令,或者定期执行命令
Future接口和实现Future接口的FutureTask类代表异步计算的结果
Runnable接口和Callable接口的实现类,都可以被ThreadPoolExecutor或ScheduleThreadPoolExecutor执行
2.3 Executor框架成员
主要成员:ThreadPoolExecutor、ScheduleThreadPoolExecutor、Future接口、Runnable接口、Callable接口、Executors(工具类)
2.3.1 TheadPoolExecutor
ThreadPoolExecutor通常使用工厂类Executors来创建。Executors可以创建3种类型的ThreadPoolExecutor类:FixedThreadPool、SingleThreadExecutor、CachedThreadPool
FixedThreadPool:创建使用固定线程数的线程池; 适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场景,它适用于负载比较重的服务器;
SingleThreadExecutor:创建使用单个线程的线程池; 适用于需要保证顺序的执行各个任务,并且在任意时间点,不会有多个线程是活动的应用场景;
CachedThreadPool:创建一个会根据需要创建新线程的线程池; 适用于执行很多的短期异步任务的小程序或者是负载比较轻的服务器;
2.3.2 ScheduledThreadPoolExecutor
ScheduleThreadPoolExecutor通常使用工厂类Executors来创建。Executors可以创建3种类型的ScheduleThreadPoolExecutor类:
ScheduleThreadPoolExecutor:创建固定个数线程的线程池; 适用于需要多个后台线程执行周期任务,同时为了满足资源管理的要求而需要限制后台线程的数量的引用场景;
SingleThreadScheduleExecutor:适用于需要单个后台线程执行周期任务,同时需要保证顺序的执行各个任务的场景
2.3.3 Future接口
Future接口和实现Future接口的FutureTask类用来表示异步计算的结果。当我们把Runnable接口或者Callable接口的实现类通过 submit 方法提交给线程池后,线程池会返回给我们一个FutureTask对象;
2.3.4 Runnable和Callable接口
Runnable接口和Callable接口的实现类,都可以被线程池执行。 它们之间的区别是Runnable不会返回结果,而Callable可以返回结果(体现在run方法上;另外:如果使用了Callable创建任务,那么线程池需要使用submit执行任务,不能使用execute)
除了自己可以创建Callable接口的对象外,我们还可以通过Executors工具类,将Runnable封装成一个Callable:
把一个Runnable包装成一个Callable:submit之后,通过FutureTask.get()方法获取到的结果为null
把一个Runnable和一个待返回结果(T)包装成Callable:submit之后,通过FutureTask.get()方法获取到的结果为T
三、ThreadPoolExecutor详解
Executor框架最核心的类是ThreadPoolExecutor,它是线程池的实现类,主要由下面4个组件构成:
corePool:核心线程池的大小
maximumPool:最大线程池的大小
BlockingQueue:用来暂时保存任务的工作队列
RejectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时(达到了最大线程池大小且队列已满),execute()方法将要调用Handler
3.1 FixedThreadPool详解
public static ExecutorService newFixedThreadPool(int nThreads){ return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLSECONDS, new LinkedBlockingQueue<Runnable>()); }
FixedThreadPool使用无界队列 LinkedBlockingQueue 作为线程池工作队列(队列的容量为Integer.MAX_VALUE)。使用无界队列为工作队列会对线程池带来如下影响:
当线程池中的线程数达到corePoolSize后,新任务将会在无界队列中等候,因此线程中的线程数不会超过corePoolSize
由于1,使用无界队列时maximumPoolSize将是一个无效参数
由于1和2,使用无界队列时keepAliveTime将是一个无效参数
由于使用无界队列,运行中的FixedThreadPool不会拒绝任务
3.2 SingleThreadExecutor详解
SingleThreadExecutor是使用单个worker线程的Executor。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
该线程池中的corePoolSize与maximumPoolSize均设置为1 ,保证每个时刻只有一个线程在运行
3.3 CachedThreadPool详解
CachedThreadPool是一个会根据需要创建新线程的线程池。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
corePoolSize被设置为0,即corePoolSize为空;maximum被设置为Integer.MAX_VALUE,则认为是无界的
四、ScheduledThreadPoolExecutor详解
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,它主要用于在给定的延迟之后运行任务或者定期执行任务;
ScheduledThreadPoolExecutor会把待调度的任务放到一个DelayQueue队列中;
执行步骤:
线程1从DelayQueue中获取到已到期的ScheduledFutureTask(DelayQueue.take())
线程1执行这个ScheduledFutureTask
线程1修改ScheduledFutureTask的time变量为下次将要被执行的时间
线程1把修改time后的ScheduledFutureTask放回到DelayQueue中,等待下次执行
五、FutureTask
5.1 简介
FutureTask除了实现Future接口之外,还实现了Runnable接口。因此,FutureTask可以交给Executor执行,也可以由调用线程直接执行(FutureTask.run());
5.2 使用
可以把FutureTask交给Executor执行;
也可以通过ExecutorService.submit()方法返回一个FutureTask,然后执行FuntureTask.get()或者cancel()方法;
除此之外,还可以单独使用FutureTask
5.3 实现
FutureTask的实现基于AbstrackQueuedSynchronizer(简称:AQS)。
AQS是一个同步框架,它提供通用机制来原子性管理同步状态、阻塞和唤醒线程,以及维护被阻塞线程的队列; 基于AQS实现的同步器包括:ReentrantLock、ReentrantReadWriteLock、CountDownLatch和FutureTask;
每一个基于AQS实现的同步器都会包括两个类型的操作:
至少一个acquire操作。这个操作阻塞调用线程,除非/直到AQS的状态允许这个线程继续执行。FutureTask的acquire操作为get()、get(long timeout, TimeUnit unit) 方法
至少一个release操作。这个操作改变AQS的状态,改变后的状态可允许一个或者多个阻塞线程被解除阻塞。FutureTask的release操作包括run()方法和cancek()方法