前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Executor框架

Executor框架

作者头像
SuperHeroes
发布2019-03-12 14:45:48
5230
发布2019-03-12 14:45:48
举报
文章被收录于专栏:云霄雨霁云霄雨霁

无限制创建线程的不足:

  • 线程生命周期的开销。线程的创建和销毁是有代价的,根据平台的不同,实际开销也不同。
  • 资源消耗。活跃的线程会消耗系统资源,尤其是内存。而且大量线程在竞争CPU资源时还将残生其他性能开销。
  • 稳定性:不同的平台最多可创建线程的数量限制不同,,并且受多个因素制约,包括JVM启动参数、Thread构造函数中请求栈的大小等。

为什么使用线程池?

  • 将所有任务放在单个线程中串行执行:糟糕的响应时间和吞吐量
  • 为每个任务分配一个线程:资源管理的复杂性

Executor框架:

代码语言:javascript
复制
Public interface Executor{
    void execute(Runnable command);
}

Executor 基于生产者-消费者模式,将任务的提交过程和执行过程解耦开来,提交任务的操作相当于生产者(生产待完成的工作单元);执行任务的线程相当于消费者(执行完这些工作单元)。

线程池:

线程池指管理一组同构工作线程的资源池。线程池与工作队列(Work Queue)密切相关。其中工作队列中保存了所有等待执行的任务。工作者线程的任务很简单:从工作队列中获取一个任务,执行任务,然后返回线程池并等待下一个任务。

Java类库提供了一个灵活的线程池以及一些有用的配置。可以通过调用Executors中的静态工厂方法来创建一个线程池:

  • newFixedThreadPool :创建一个固定长度的线程池,每当提交一个任务时就创建一个线程,直到达到线程池的做大数量。这时线程池规模将不再发生改变(如果某个线程由于发生了未预期的Exception而结束,那么线程池会补充一个新的线程。
  • newCachedThreadPool:将创建一个可缓存的线程池,如果线程池的当前规模超过了处理需求时,那么将回收空闲的线程,而当需求增加时,可以添加新的线程。
  • newSingleThreadExecutor:单线程的Executor。它创建单个工作者线程来执行任务,如果这个线程异常结束,会创建另一个线程来替代。能确保依照任务在队列中的顺序来串行执行。
  • newScheduledThreadPool:创建一个固定长度的线程池,而且以延迟或定时的方式来执行任务。类似与Timer

通过使用Executor,可以实现各种调优、管理、监视、记录日志、错误报告和其他功能。

Executor的生命周期:

如何关闭Executor?如果不关闭Executor,那么JVM将永远不会结束(JVM在所有非守护线程结束后太会退出)。

  • 平缓关闭模式:完成所有已启动的任务,并且不再接收新任务
  • 暴力关闭模式:直接关掉电源

为了解决执行服务的生命周期问题,Executor扩展了ExecutorService接口,添加了一些用于生命周期管理的方法。

ExecutorService 的生命周期有三种状态:运行、关闭和已终止。ExecutorService 在初始创建时处于运行状态。

代码语言:javascript
复制
Public interface ExecutorService extends Excutor{
    void shutdown();    //执行平缓关闭过程----不再接收新任务,同时等待已提交任务的完成,包括队列中还未开始执行的任务
    List<Runnable> shutdownNow();    //执行粗暴关闭过程----它尝试取消所有运行中的任务,并不在启动队列中尚未开始的任务
    boolean isShutdown();    //判断是否关闭
    boolean isTerminated();    //判断是否终止
    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedExecption;    //等待终止
    //其他用于任务提交的遍历方法
    //…
}

Executor执行任务有4个生命周期阶段:创建、提交、开始和完成。由于一些任务可能需要执行很长时间,因此同行希望能够取消此类任务。Executor框架中:

  • 已提交但尚未开始的任务可以取消
  • 已经开始的任务,只有当它们能相应中断时,才能取消
  • 取消已经完成的任务没有任何影响,即一个任务完成后将永远停留在完成状态,无法撤回。因为任务的生命周期只能前进,不能后退。

一般在调用awaitTermnation之后立即调用shutdown,从而产生同步关闭ExectuorService的效果。

Callable和Future:

Executor使用Runnable作为其基本的任务表达形式。Runnable是一种有很大局限的抽象,虽然run能写入日志文件或者将结果放入某个共享的数据结构,但他不能返回一个值或者抛出一个受检查的异常。

Callable是一种更好的抽象:它认为主入口点(即call)将返回一个值,并可以抛出异常。

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

代码语言:javascript
复制
Public interface Callsble<V>{
    V Call() throws Exception;
}

Public interface Future<V>{
    boolean cancel (boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException,  CancellationException;
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,  CancellationException, TimeoutExecption;
}

ExecutorService中所有submit方法都将返回一个Future。将一个Runnable或Callable提交给Executor, 返回一个Future用来获得任务的执行结果或者取消任务。

CompletionService: Executor 与 BlockingQueue

完成任务(CompletionService):如果向Executor提交了一组计算任务,并希望在计算完成后获得结果。可以使用ExecutorService,获取返回的Future反复轮询get方法。但使用CompletionService更加方便。

CompletionService将Executor和BlockingQueue融合在一起。将执行的结果放入BlockingQueue中。然后用take和poll等方法获取完成后的结果。

ExecutorCompletionService实现了C欧美拍了体哦那Service,并将计算部分委托给一个Executor。

参考资料:

https://blog.csdn.net/chuan_yu_chuan/article/details/53390737 http://www.importnew.com/24923.html

任务取消:

什么时候会取消一个任务:

  • 用户请求取消
  • 有时间限制的操作
  • 应用程序事件
  • 错误
  • 关闭
代码语言:javascript
复制
@ThreadSafe
Public class Test implements Runnable{
    private volatile boolean cancelled;
    public void run(){
        while(!cancelled){
            //具体逻辑
       }
    }
    public void cancel(){  cancelled = true;  }
}

取消策略:

包括三方面:

  • How: 其他代码如何请求取消任务    ----通过调用cancel()方法
  • When: 任务何时检查是否取消了请求    ----每次执行具体逻辑前判断
  • What: 相应取消时应执行哪些动作    ----如果取消则退出

任务中断:

上面的任务取消有一个严重的问题:如果任务调用了一个阻塞方法,那么任务有可能永远不会检查取消标志,因此线程永远不会结束。比如生产者消费者模式中生产者的具体任务逻辑是:

blockingQueue.put(n++);

如果消费者执行效率远小于生产者效率,那么队列满时生产者线程将会阻塞。然后取消任务执行消费者线程被取消掉,那么生产者线程将会永远阻塞,无法检查取消标志。

所以应该使用任务中断方式。中断是实现取消的最合理的方式。

每个线程都有一个boolean类型的中断状态,当中断线程时,这个线程的中断状态将被置为true。

代码语言:javascript
复制
Public class Thread{
    public void interrupt(){ … }
    public boolean isInterrupted(){ … }
    public static boolean interrupted(){ …  }
    …
}

对中断操作的正确理解是:它并不会真正地中断一个正在运行的线程,而只是发出中断请求,然后由线程在下一个合适的时刻中断自己。

代码语言:javascript
复制
Public class Test implements Runnable{
    public void run(){
        while(!Thread.currentThread().isInterrupted()){
            //具体逻辑
       }
    }
    public void cancel(){  interrupt();  }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018/11/01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 无限制创建线程的不足:
  • 为什么使用线程池?
  • Executor框架:
    • 线程池:
      • Executor的生命周期:
        • Callable和Future:
          • CompletionService: Executor 与 BlockingQueue
            • 参考资料:
          • 任务取消:
            • 取消策略:
          • 任务中断:
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档