Executor框架
Executor框架的类与接口
Executor框架的使用的示意图
介绍ThreadPoolExcutor、ScheduledThreadPoolExcutor、Future接口、Runnable接口、Callable接口、
Executors。
ThreadPoolExecutor
ThreadPoolExcutor通常使用工厂类Executors来创建。Executors可以创建3种类型的ThreadPoolExcutor:
SingleThreadExecutor、FixedThreadPool和ScheduledThreadPoolExcutor。
FixedThreadPool可重用固定线程数的线程池。下面是其工厂的代码:
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nTheads, nThreads, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
corePoolSize和maximumPoolSize被置为nThreads。当线程数大于corePoolSize时,keepAliveTime为多余
的空闲线程等待新任务的最长时间,超过这个时间的多余线程将被终止。这里把keepAliveTime设为0L,意
味着多余的空闲线程会被立刻终止。
适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场景,它适用于负载比较重的服务器。
使用无界队列LinkedBlockingQueue作为线程的工作队列(即存放任务的队列),队列容量为Integer.MA-
X_VALUE。因使用无界队列,所以可以不用因为任务太多队列无法容纳而去增加工作线程或者拒绝任务。
SingleThreadExecutor
只有一个工作线程的Executor,下面是其工厂的代码:
public static ExecutorService newSingleTHreadExecutor(){
return new FinalizableDlegateExecutorService(new ThreadPoolExecutor(1, 1, 0L,
TimeUit.MILLISECONDS, new LinkedBlockingQueue<Runnable>));
}
适用于需要保证顺序地执行各个任务,并在任意时间点,不会有多个线程在活动的应用场景。
SingleThreadExecutor就是corePoolSize和maximumPoolSize都为1的FixedThreadPool。
同样使用的是无界工作队列。
CachedThreadPool
一个根据工作需要创建线程的线程池,其工厂的代码如下:
public static ExecutorService newCachedThreadExecutor(){
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L,
TimeUit.MILLISECONDS, new SynchronousQueue<Runnable>());
是无界的线程池,适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器。
可以使各个短期任务的完成时刻上,时间相差更短。
corePoolSize为0,maximuPoolSize被设置为Integer.MAX_VALUE,keepAliveTime为60,意味着在没有
任务的时候,所有线程都会被终止掉。这就是使用与短期任务的原因,在短期任务突然增加的时候就大量初始化线程,而之后因会有较长时间没有任务,而不让线程自旋空等白白浪费cpu资源。
而且其工作队列为SynchronousQueue,看上一篇文章可知,这个不存元素的队列,在任务被提交上来
时,必须有人来取走,提交任务的线程才能返回,而这个线程池用这个队列的原因在于它会不断的创建
线程来尽快取走这些任务,就是说如果主线程提交任务的速度高于线程池中的线程处理任务的速度,
CachedThreadPool会不断创建新线程。极端情况下,CachedTheadPool会创建因过多线程而耗尽CPU
资源和内存资源。
ScheduledThreadPoolExecutor
通常使用工厂类Exectutors来创建。Executors可以创建两种类型的ScheduledThreadPoolExecutor,如下:
* ScheduledThreadPoolExecutor 包含若干线程的ScheduledThreadPoolExecutor
*SingleThreadScheduledExecutor 包含一个线程的ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor适用于需要多个后台线程执行周期任务,同时为了满足资源管理的需求而
需要限制后台线程的数量的应用场景。
SingleThreadScheduledExecutor适用于需要单个后台线程执行周期任务,同时需要保证顺序地执行各个
任务的应用场景。
ScheduledThreadPoolExecutor的功能与Timer类似,但功能更强大,更灵活。Timer对应的是单个后台线程,
而ScheduledThreadPoolExecutor可以执行多个后台线程。
ScheduledThreadPoolExecutor的实现
主要成员
long time:表示这个任务将要被执行的绝对时间;
long swquenceNumber:表示这个任务被添加到该Exectutor的序号。
long period:表示任务执行的间隔周期。
调用ScheduledThreadPoolExecutor的scheduledAtFixedRate()和scheduledWithFixedDelay()把任务添加
到工作队列时,会将任务封装到实现了RunnableScheduledFuture接口的ScheduledFutureTask,并将
ScheduledFutureTask加入工作队列DelayQueue中,而DelayQueue是封装了一个PriorityQueue的。这个
PriorityQueue会对队列中的ScheduledFutureTask进行排序,time小的在前(时间早先被执行),time相同则看
添加顺序(即查看序号)。
获取队列元素:
获取锁,PriorityQueue为空时,无限时进入等待队列。若task的time比当前时间大,则进入等待队列等到时间为time。否则
就如果取出一个元素后,队列不为空,就唤醒等待所有线程,然后释放锁,并返回一个元素task。
添加队列元素:
获取锁,添加新任务,唤醒等待队列中的所有线程,释放锁。
FutureTask
Future接口及实现了Future接口的FutureTask类,代表了异步计算结果。
ExecutorService有三个方法,分别是submit(Runnable)、submit(Runnable, T result)、submit(Callable<T>)。
三个方法都会返回一个Future,调用Future#get()也可以在未执行完时,进入阻塞状态。但是get()返回的
结果不一样,分别是返回null,返回result,返回一个T类型对象。
FutureTask有三种状态。
* 未启动。FutureTask#run()方法未被执行之前。
* 已启动。FutureTask#run()方法被执行过程中。
* 已完成。FutureTask#run()方法执行完后正常结束或者被取消或执行run()抛异常而结束。
使用FutureTask注意点:
1.处于未启动或已启动状态时调用get()方法将导致调用线程阻塞
2.处于已完成状态时,执行get()方法时,立即返回结果或抛出异常。调用cancel方法返回false
3.处于未启动状态时,执行cancel()方法将导致此任务永远不被执行。
4.cancel(true)时,将以中断执行此任务的线程的方式来试图停止任务,cancel(false)则不会产生影响,即
其参数为false时,cancel方法无效。
FutureTask除了在Executor框架中使用,还可以单独使用。FutureTask也实现了Runnable接口。
-----------------------------------------------------------------------------------------------------------------------------------------------