chapter06_任务执行_2_Executor框架

  • (1) "1_在线程中执行任务.md"中说到, 串行执行的问题是糟糕的吞吐量和响应性能; 为每个任务分配一个线程的问题是资源消耗过高, 因此好的方式是使用线程池

    (2) Java中, 任务执行的主要抽象不是Thread, 而是Executor(java.util.concurrent包中), 它是一个接口, 只提供了一个方法

      public interface Executor {
    
          /**
           * Executes the given command at some time in the future.  The command
           * may execute in a new thread, in a pooled thread, or in the calling
           * thread, at the discretion of the {@code Executor} implementation.
           *
           * @param command the runnable task
           * @throws RejectedExecutionException if this task cannot be
           * accepted for execution
           * @throws NullPointerException if command is null
           */
          void execute(Runnable command);
      }
    

    (3) Executor在java.util.concurrent中的继承接口与实现类见"DelegatedScheduledExecutorService.uml"

    (4) Executor基于__生产者-消费者模式__, 提交任务的操作相当于生产者, 执行任务的线程相当于消费者(设计模式中的"命令模式")

    (5) 使用Executor实现简单的服务器程序

      public class TaskExecutionWebServer {
    
          private static final int NTHREADS = 100;
          private static final Executor exec = Executors.newFixedThreadPool(NTHREADS);
    
          public static void main(String[] args) throws IOException {
    
              ServerSocket socket = new ServerSocket(80);
    
              while (true) {
                  final Socket connection = socket.accept();
    
                  Runnable task = new Runnable() {
                      public void run() {
                          handleRequest(connection);
                      }
                  };
    
                  exec.execute(task);
              }
          }
    
          private static void handleRequest(Socket connection) {
              // request-handling logic here
          }
      }
    
  • 由于Executor是一个接口, 因此事实上我们可以用Executor自己实现串行执行方式 和 为每个任务开新线程的方法

    1° 串行执行方式的Executor实现

      public class WithinThreadExecutor implements Executor {
    
          public void execute(Runnable r) {
              r.run();
          }
      }
    

    2° 为每个任务开新线程的Executor实现

      public class ThreadPerTaskExecutor implements Executor { 
    
          public void execute(Runnable r) {
      
              new Thread(r).start();
          }
      }
    

    所以, 事实上各种Executor的实现就是定义了几种常用的模式供调用

  • 执行策略

    (1) 执行策略定义了如下几个问题

    1° 在什么线程执行任务

    2° 任务按照什么顺序执行(FIFO, LIFO, 优先级?)

    3° 有多少个任务可以并发执行

    4° 在队列中有多少个任务在等待执行

    5° 如果系统由于过载而需要拒绝一个任务,那么应该选择哪一个

    6° 在执行一个任务之前或之后,应该进行哪些操作

    (2) Executor通过将任务的提交与任务的执行策略分离开,有助于在部署阶段选择与可用硬件资源最匹配的执行策略

    (3) 当希望获得一种更灵活的执行策略时, 应该使用Executor代替Thread

  • 线程池

    (1) "在线程池中执行任务"相比"为每个任务分配一个线程"的优势

    1° 在处理多个请求时, 分摊在线程创建和销毁过程中产生的巨大开销

    2° 当请求到达时, 工作线程通常已经存在, 因此不会由于等待创建线程而延迟任务的执行, 从而__提高了响应性__

    3° 通过适当调整线程池的大小, 可以创建足够多的线程以使处理器保持忙碌状态;同时防止过多线程互相竞争资源

    (2) Executors提供了静态工厂方法用于创建多种不同类型的线程池

    1° newFixedThreadPool: 每当提交一个任务时创建一个线程, 直到达到预设的线程池的最大数量

    2° newCachedThreadPool: 如果线程池的当前规模超过了处理需求,则回收空闲的线程; 如果需求增加, 则可以添加新的线程

    3° newSingleThreadExecutor: 创建单线程的Executor,保证同一时刻只能有一个任务被执行

    4° newScheduledThreadPool: 创建一个固定长度的线程池, 并且以延迟或定时的方式执行任务

    (3) 所有的这些线程池对象, 都继承和实现了Executor接口, 见"DelegatedScheduledExecutorService.uml"

  • ExecutorService

    (1) 由于Executor采用__异步__方式执行任务, 在任意时刻提交的任务可能正在运行, 可能运行完成, 可能还在等待; 此时就会有一种需求: 关闭未执行的任务

    于是, ExecutorService扩展了Executor接口, 用来管理执行服务的生命周期

    (2) ExecutorService接口提供的一些方法

      void shutdown();
      List<Runnable> shutdownNow();
      boolean isShutdown();
      boolean isTerminated();
      boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
    

    (3) ExecutorService的生命周期有3种状态: 运行、关闭、终止

    shutdown()用来平稳的关闭任务, 让已经提交的任务执行完;

    shutdownNow()用来粗暴的关闭任务, 尝试取消所有正在执行的任务, 提交但未执行的任务取消之

    等所有任务都完成后, ExecutorService将进入终止状态

  • 延迟任务与周期任务

    (1) Timer类曾经用于管理__延迟任务__(xxx时间后执行某任务) 和 周期任务(每隔xxx时间执行一次某任务), 但是存在一系列的缺陷

    (2) Timer类的缺陷

    1° Timer支持基于绝对时间的调度机制, 因此任务的执行对系统时钟变化很敏感

    2° Timer在执行所有定时任务时只会创建一个线程, 因此如果一个任务执行过长, 会影响后面任务的执行

    3° Timer线程不会捕获异常, 因此一个任务抛出异常, 整个Timer线程都会被取消, 后面的任务彻底执行不了了

    示例

      import static java.util.concurrent.TimeUnit.SECONDS;
    
      public class OutOfTime {
    
          public static void main(String[] args) throws Exception {
    
              Timer timer = new Timer();
    
              timer.schedule(new ThrowTask(), 1);
              SECONDS.sleep(1);
    
              timer.schedule(new ThrowTask(), 1);
              SECONDS.sleep(5);
          }
    
          static class ThrowTask extends TimerTask {
    
              public void run() {
    
                  throw new RuntimeException();
              }
          }
      }
    

    这个示例的执行结果是1秒后就结束, 因为第二个timer.schedule(new ThrowTask(), 1);因为Timer已经取消所有根本执行不了

    (3) 所以, 不应该用Timer了, 应该用ScheduledThreadPoolExecutor和DelayQueue实现延迟任务和周期任务

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值