天天看点

任务执行(第六章)

任务边界:当围绕“任务执行”来设计应用程序时,第一步就是要找出清晰的任务边界。

线程生命周期的开销非常高:线程的创建于销毁

资源消耗:活跃的线程会消耗系统资源,尤其是内存。如果可运行的线程数量多于可用处理器的数量,那么有些线程将闲置。所以,如果已经拥有足够多的线程使CPU保持忙碌状态,那么创建再多的线程反而会降低性能

稳定性:在可创建线程的数量上存在一定限制,这个闲置值将随平台的不同而不同,并且受多个因素闲置。如果破坏了这些限制,那么可能抛出OutOfMemoryError异常

Executor框架能支持多种不同类型的任务执行策略,它提供了一种标准的方法将任务的提交过程与执行过程解耦,并用Runnable来表示任务。Executor的实现还提供了对生命周期的支持,以及统计信息收集、应用程序管理机制和性能监视等机制。

Executor基于生产者----消费者模式,提交任务的操作相当于生产者,执行任务的线程相当于消费者。

Executor的生命周期

JVM只有在所有(非守护)线程都终止后才会退出,因此,如果Executor没有正确关闭,那么JVM将无法退出。

Executor执行的任务有4个证明周期阶段:创建、提交、开始和完成。已提交但尚未开始的任务可以取消,但对于已经开始执行的任务,只有当他们响应中断时才能取消。

Executor扩展了ExecutorService接口,用于管理生命周期:

Executor的缺陷:

Executor使用Runnable作为基本的任务标志形式,但Runnable中的run()不能返回值也不能抛出异常

在执行策略中定义了任务执行的“What、Where、When、How”等方面。包括:

线程池指管理一组同构工作线程的资源池。

可以通过调用Executor中的一些静态函数创建线程池:

newFixedThreadPool:创建一个固定长度的线程池,每当提交一个任务时就创建一个线程,直到创建的线程数量达到最大,此后线程的数量不会再变化。如果某个线程由于发生未预期的Exception而结束,那么线程会补充一个新的线程

newCachedThreadPool:创建一个可缓存的线程池,如果线程池的规模超过了处理需求,那么将回收空闲的线程,而当需求增加时,则可以添加新的线程,线程池的规模不存在人任何限制

newSingleThreadExecutor:单线程的Executor,它创建单个工作者线程来执行任务,如果这个线程异常结束,会创建一个新的线程来代替,其能确保依照任务在队列中的顺序串行序执行

new ScheduledThreadPool:创建一个固定长度的线程池,而且以延时或定时的方式来执行顺序

Timer类负责管理延迟任务以及周期任务,但是Timer类存在一些缺陷,应该考虑使用ScheduledThreadPool类代替它。

Timer类的缺陷

Timer在执行所有定时任务时只会创建一个线程,如果某个任务的延时时间过长,那么将破坏其他TimerTask的定时精确性。

如果TimerTask抛出了一个未受检查的异常,由于Timer线程并不捕获异常,因此Timer将终止线程的执行。

Callable提供了一种相比Runnable更好的抽象:call,call()有返回值(要用Callable表示无返回值的任务,使用Callable),并且允许抛出异常。

Future表示一个任务的生命周期,并提供了相应的方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等。Future意味着任务的生命周期只能前进,不能后退。

如果任务抛出了异常,那么get将异常封装为ExecutorException并重新抛出,并且可以通过getCause来获得被封装的初始异常,如果任务被取消,那么get将抛出 CancellationException