首页 > 代码库 > Executor框架
Executor框架
任务的执行
Java多线程并发下大多围绕着执行任务进行管理的,所谓任务就是抽象的、离散的工作单元。这样将工作分为任务的创建和任务的执行,而且理想状态下任务是独立的活动,它的工作不依赖于其它任务的状态、结果和边界效应。任务的独立有利于并发性,如得到相应的资源,从而可以并行的执行。
任务的执行策略:顺序执行、每任务每线程
顺序执行:在一个线程中,按照顺序依次执行任务。
缺点:糟糕响应性和吞吐量。
每任务每线程:为每一个任务都创建一个线程,让这个子线程去执行任务。
缺点:无限制创建线程,会带来以下问题
【线程生命周期的开销】创建和销毁线程是需要开销的
【资源消耗】 活动资源会消耗系统资源,尤其是内存。如果可运行的线程多余可用的处理器数量,线程就会被闲置,所以需要合理的控制创建的线程的数量。
补充:线程限制的因素:依据不同平台来定,同时也受到JVM启动参数、Thread的构造函数中请求的栈的大小等因素的影响,以及底层操作系统线程的限制。不过在32位机器上,主要的限制因素还是线程栈的地址空间。每个线程都维护着两个执行栈(execution stack),一个用于Java代码,另一个用于原生代码。典型的JVM默认会产生一个组合栈,大小在半兆字节左右。(你可以通过-Xss JVM参数或者通过Thread的构造函数修改这个值)如果你为你的线程分配了232字节的栈,那么线程的数量将被限制在几千到几万不等。
”每任务每线程“(Thread-per-Task)方法的问题在于没有对线程的数量进行限制,会给资源管理带来麻烦。
Executor框架
任务是逻辑上的工作单元,线程是任务使任务异步执行的机制。利用线程池去解决线程的管理,Executor提供了多种线程池的实现。在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免this逃逸问题——如果我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时可能会访问到初始化了一半的对象用Executor在构造器中。所以,请考虑用executor代替new Thread.start( );这样的代码。
在JAVA中首先抽象的不是Thread,而是Executor。Executor只是一个简单的接口,但它却为一个灵活而且强大的框架创造了基础,这个框架可以用于异步任务的执行,而且支持很多不同类型的任务执行策略。它还为任务提交和任务执行之间解耦合提供了标准方法,为使用Runnable描述任务提供了通用的方式。Executor的实现还提供了对生命周期管理的支持以及钩子函数,可以添加诸如统计收集、应用程序管理机制和监视器等扩展。
Executor框架包括:Executor,线程池,Executors,ExecutorService,CompletionService,Future,Callable等对象。
Executor只是一个简单的包含execute( )方法的接口;
Executors是一个包含很多静态工具方法的工具类,可以用Executors创建一个Executor;
ExecutorService接口继承了Executor,是Executor接口的一个扩展,添加了executor生命周期的管理等功能;
ComplementService是一个完成服务接口,它的唯一实现类是ExecutorCompletionService,它将任务交由executor处理,并且它可以将已经完成的任务和未完成的任务通过BlockingQueue队列分隔开来;
Future是一个异步计算的结果,利用Future可以获取该任务执行的结果、或取消该任务、或获取任务的执行状态;
Callable是对于任务的描述,相对与Runnable,但是可以返回任务执行结果以及抛出受检查的异常。
线程池
从每任务每线程策略迁移到线程池策略,会大大提高应用程序的稳定性。
线程池是用来帮助管理线程的,在线程池中执行任务,有着“每任务每线程”无法比拟的优势,重用线程池中已经存在的线程,而不是去创建新的线程,这样可以在处理多请求的任务中抵消线程创建、销毁产生的开销。另外,线程池会提高响应的速度,因为当请求到来的时候,线程已经存在,省去了线程创建的时间。
Executors中提供了一些静态的方法去创建不同特点的线程池
newFixedThreadPool创建一个定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大值,这是线程池的长度就不会增加,只有当一个线程结束后后继任务才能执行。
newCachedThreadPool创建一个可以缓存的线程池,如果当前的线程长度超过了处理的需要时,它可以灵活的回收空闲的线程,它可以灵活的增加新的线程,不需要指定线程池的大小。
newSingleThreadExecutor创建一个单线程化的Executor,它只创建唯一的工作线程来执行任务,如果这个线程异常结束,它会创建一个新的线程来替换它。它会保证任务按照任务队列规定的顺序执行,单线程化的executor同样使用了大量的内部同步,以保证任何任务的写操作对后续线程是可见的,这就意味着对象可以安全的限制在“任务线程”中,尽管这个单一的线程是不断交替更新的。
newScheduledThreadPool创建一个定长的线程池,而且支持延迟周期性的任务,类似于Timer类。
生命周期(ExecutorService)
JVM会在所有线程全部终止后才退出,如果无法关闭Executor将会造成JVM无法关闭。
为了解决executor生命周期这个问题,ExecutorService接口扩展了Executor,并添加了一些用于生命周期管理的方法
executorService暗示了声明周期的3种状态:运行(Running)、关闭(shutting down)、终止(terminated)。
最初创建后是运行状态,可以接受新的任务,并且不断的执行任务。
关闭后进入关闭状态,不再接受新的任务,但是仍然运行已经提交的任务。这是关闭过程的一个缓冲,是平缓的关闭。
终止状态,不接受新任务,也没有任务在运行。
其中shutdownNow方法是一个强制的关闭过程,没有中间的关闭状态,直接终止所有任务的执行。
延迟并具有周期性的任务
参见Timer与ScheduledThrealpoolExecutor
增强型并发任务Callable和Future
参见Callable和Fututre
Executor框架
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。