Java多线程之Executor框架和手写简易的线程池(4)

该线程池中corePoolSize和maximumPoolSize都为1,表示始终只有一个线程在工作,适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景。同时使用无界阻塞队列,当任务多时极有可能OOM。

CachedThreadPool
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>()

CachedThreadPool类型的线程池corePoolSize为0,表示任务将会提交给队列,但是SynchronousQueue又是一个不包含任何容量的队列。所以每一个任务提交过来都会创建一个新的线程来执行,该类型的线程池适用于执行很多的短期异步任务的程序,或者是负载较轻的服务器。如果当任务的提交速度一旦超过任务的执行速度,在极端情况下可能会因为创建过多线程而耗尽CPU和内存资源。

ScheduledThreadPool

对于定时任务类型的线程池,Executor可以创建两种不同类型的线程池:ScheduledThreadPoolExecutor和SingleThreadScheduledExecutor,前者是包含若干个线程的ScheduledThreadPoolExecutor,后者是只包含一个的ScheduledThreadPoolExecutor。

ScheduledThreadPoolExecutor适用于需要多个后台线程执行周期任务,同时为了满足资源管理的需求而需要限制后台线程的数量的应用场景。

SingleThreadScheduledExecutor适用于需要单个后台线程执行周期任务,同时需要保证顺序地执行各个任务的应用场景。
new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());

在对该类型线程池进行实例化时,我们可以看到maximumPoolSize设置为了Integer的最大值,所以很明显在极端情况下和CachedThreadPool类型一样可能会因为创建过多线程而耗尽CPU和内存资源。

DelayedWorkQueue是一种延时阻塞队列,此队列的特点为其中元素只能在其延迟到期时才被使用。ScheduledThreadPool类型在执行任务时和其他线程池有些不同。
1.ScheduledThreadPool类型线程池中的线程(假设现在线程A开始取任务)从DelayedWorkQueue中取已经到期的任务。
2.线程A获取到任务后开始执行。
3.任务执行完成后设置该任务下一次执行的时间。
4.将该任务重新放入到线程池中。

ScheduledThreadPool中存在着定时任务和延时任务两种。

延时任务通过schedule(...)方法以及重载方法和scheduleWithFixedDelay实现,延时任务通过设置某个时间间隔后执行,schedule(...)仅执行一次。

定时任务由scheduleAtFixedRate实现。该方法创建并执行在给定的初始延迟之后,随后以给定的时间段进行周期性动作,即固定时间间隔的任务。

特殊的scheduleWithFixedDelay方法是创建并执行在给定的初始延迟之后首先启用的定期动作,随后在一个执行的终止和下一个执行的开始之间给定的延迟,即固定延时间隔的任务。

固定时间间隔的任务不论每次任务花费多少时间,下次任务开始执行时间是确定的。对于scheduleAtFixedRate方法中,若任务处理时长超出设置的定时频率时长,本次任务执行完才开始下次任务,下次任务已经处于超时状态,会马上开始执行。若任务处理时长小于定时频率时长,任务执行完后,定时器等待,下次任务会在定时器等待频率时长后执行。

固定延时间隔的任务是指每次执行完任务以后都等待一个固定的时间。由于操作系统调度以及每次任务执行的语句可能不同,所以每次任务执行所花费的时间是不确定的,也就导致了每次任务的执行周期存在一定的波动。

需要注意的是定时或延时任务中所涉及到时间、周期不能保证实时性及准确性,实际运行中会有一定的误差。

Callable/Future

在介绍实现多线程的时候我们有简单介绍过Runnable和Callable的,这两者基本相同,不同在于Callable可以返回一个结果,而Runnable不返回结果。对于Callable接口的使用方法和Runnable基本相同,同时我们也可以选择是否对结果进行接收处理。在Executors中提供了将Runnable转换为Callable的api:Callable<Object> callable(Runnable task)。

Future是一个用于接收Runnable和Callable计算结果的接口,当然它还提供了查询任务状态,中断或者阻塞任务以及查询结果的能力。
boolean cancel(boolean mayInterruptIfRunning)  //尝试取消执行此任务。 
V get()  //等待计算完成,然后检索其结果。 
V get(long timeout, TimeUnit unit) //等待最多在给定的时间,然后检索其结果(如果可用)。 
boolean isCancelled() //如果此任务在正常完成之前被取消,则返回 true 。 
boolean isDone() //如果任务已完成返回true。 

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/5a88e4496d8fb0a25cdc5c39ccb4ec72.html