您现在的位置是:首页 > 文章详情

java并发编程实战 第六章 任务执行

日期:2018-04-20点击:345

一、在线程中执行任务

1、串行地执行任务

当服务器正在处理请求时,新的连接必须等待直到请求处理完毕。
如果请求阻塞时间过长,用户将认为服务器不可用。

2、显式地为任务创建线程

通过每个请求创建一个新的线程来提供服务,从而实现高响应性。
需要创建大量线程时:

  • 线程生命周期开销非常高,线程创建、销毁需要代价
  • 资源消耗,活跃的线程消耗系统资源,尤其是内存。如果可运行的线程数量多于可用处理器的数量,那么有些线程将闲置。
    大量闲置线程占用内存,给垃圾回收带来压力,并在竞争cpu资源时产生性能开销。
  • 稳定性。可创建线程数据存在一个限制。受JVM启动参数、Thread构造函数中请求栈大小、底层操作系统对线程的限制等。

在一定范围内,增加线程可以提高系统吞吐率,超过这个范围,在创建更多的线程只会降低程序的执行速度,甚至系统崩溃。

二、Executor框架

Executor提供了一种标准的方法将任务的提交过程与执行过程解耦,并用Runnable来表示任务。

public interface Executor { void execute (Runnbale command); } 

提供了对生命周期的支持,以及统计信息收集、应用程序管理机制和性能监视等机制。
Executor基于生产者-消费者模式,提交任务相当于生产者,执行任务的线程相当于消费者。

1、线程池

管理一组同构工作线程的资源池。比“为每个任务分配一个线程”优势更:

  • 通过重用现有的线程而不是创建新线程,
  • 可以在处理多个请求时分摊创建线程和销毁的开销。不会等待线程创建而延迟任务执行。
  • 创建足够的线程使处理器保持忙绿,防止过多线程相互竞争资源而使应用程序耗尽内存或失败
  • newSingleThreadPool:单线程的Executor,创建单个工作者线程来执行任务,如果这个线程异常结束,会创建另一个线程来替代。

    可以确保任务在队列中串行执行。
  • newFixedThreadPool:创建一个固定大小的线程池,每当提交一个任务时就创建一个线程,直到达到线程池的最大数量。
  • newCachedThreadPool:创建一个可缓存的线程池,如果线程池当前规模超过了处理需求时,将回收空闲线程;当需求增加时,课添加新的线程,线程池规模无限制
  • newScheduledThreadPool:创建一个固定大小的线程池,而且以延迟或定时的方式来执行任务,类似Timer

    public static ExecutorService newFixedThreadPool(int nThreads) {

    return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

    }
    public static ExecutorService newSingleThreadExecutor() {

    return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));

    }
    public static ExecutorService newCachedThreadPool() {

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

    }

2、Executor的生命周期

ExecutorService接口添加了生命周期管理方法。
ExecutorService的生命周期有三种状态:运行、关闭、已终止。
在初始化创建时处于运行状态。
shutdown方法将执行平缓关闭过程:不再接受新任务,同时等待已久提交的任务执行完成,包括还未开始执行的任务。
shutdownNow方法执行粗暴关闭过程:长手取消所有运行中的任务,并且不再启动队列中尚未开始执行的任务。

3、延迟任务与周期任务

三、找出可以利用的并行性

如果要使用Executor,必须将任务表述为一个Runnable。

1、携带结果的任务Callbale与Future

Executor框架使用Runnable作为其基本任务的表示形式。

Runnable存在问题:虽然run能写入到日志文件或者将结果放入某个共享的数据结构,但不能返回一个值或抛出一个受检查的异常。
许多任务实际上都是存在延迟计算--执行数据库查询、从网络上获取资源或计算某个复杂功能。
对于这种任务,Callable是一种更好的抽象:它认为主入口点将返回一个值,并可能抛出一个异常。

public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; } 

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

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

原文链接:https://yq.aliyun.com/articles/583822
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章