Executor 框架

Java 的线程既是工作单元,也是执行机制。为了更好地管理和调度线程,Java 提供了强大的 Executor 框架。

Executor 框架的两级调度模型

在 Java 的 HotSpot VM 线程模型中,Java 线程(java.lang.Thread) 被一对一映射为本地操作系统线程。Java 线程启动时会创建一个本地操作系统线程。

  • 上层调度:应用程序通常将应用分解为若干个任务,并使用用户级的调度器(Executor 框架)将这些任务映射为固定数量的线程。
  • 下层调度:操作系统内核将这些线程映射到硬件处理器上。应用程序通过 Executor 框架控制上层的调度,而下层的调度由操作系统内核控制,不受应用程序的直接控制。

Executor 框架的结构

  1. 任务
    • 任务的实现接口包括 Runnable 接口或 Callable 接口。
  2. 任务的执行
    • 任务执行机制的核心接口是 Executor,其继承接口为 ExecutorServiceExecutor 框架的两个关键实现类是 ThreadPoolExecutorScheduledThreadPoolExecutor
  3. 异步计算的结果
    • 异步计算的结果由 Future 接口及其实现类 FutureTask 表示。

Executor 框架包含的主要类与接口

  • Executor:
    • Executor 是一个接口,它是 Executor 框架的基础,负责将任务的提交与任务的执行分离。
  • ThreadPoolExecutor:
    • ThreadPoolExecutor 是线程池的核心实现类,用于执行提交的任务。
  • ScheduledThreadPoolExecutor:
    • ScheduledThreadPoolExecutor 是一个实现类,可以在给定延迟后运行命令或定期执行命令,比 Timer 更灵活,功能更强大。
  • Future 接口与 FutureTask 类:
    • Future 接口及其实现类 FutureTask 代表异步计算的结果。
  • Runnable 接口与 Callable 接口:
    • 这些接口的实现类都可以被 ThreadPoolExecutorScheduledThreadPoolExecutor 执行。

使用示意图

Executor 框架使用示意图 1 Executor 框架使用示意图 2

Executor 框架的工作流程

  1. 主线程首先创建实现 RunnableCallable 接口的任务对象。可以通过工具类 Executors 将一个 Runnable 对象封装为一个 Callable 对象。

  2. 然后将 Runnable 对象或 Callable 对象提交给 ExecutorService 执行。

  3. 如果执行 ExecutorService.submit(...)ExecutorService 会返回一个实现 Future

    接口的对象(通常是 FutureTask 对象)。

    • 由于 FutureTask 实现了 Runnable,程序员也可以创建 FutureTask,然后直接交给 ExecutorService 执行。
  4. 主线程可以通过 FutureTask.get() 方法等待任务执行完成,或通过 FutureTask.cancel(boolean mayInterruptIfRunning) 来取消任务的执行。

Executor 框架的成员

ThreadPoolExecutor

  • SingleThreadExecutor
    • 创建一个使用单个线程的 ThreadPoolExecutor
    • 适用于需要顺序执行各个任务,并且在任意时间点只有一个线程活动的场景。
    • 使用无界队列 LinkedBlockingQueue
  • FixedThreadPool
    • 创建一个使用固定线程数的 ThreadPoolExecutor
    • 适用于为了资源管理而需要限制当前线程数量的场景,如负载较重的服务器。
    • 使用无界队列 LinkedBlockingQueue
  • CachedThreadPool
    • 创建一个大小无界的线程池,适用于执行很多短期异步任务的小程序,或负载较轻的服务器。
    • 使用无容量的 SynchronousQueue 作为线程池的工作队列。

ScheduledThreadPoolExecutor

  • ScheduledThreadPoolExecutor
    • 包含多个线程的 ScheduledThreadPoolExecutor,适用于需要多个后台线程执行周期任务,并且需要限制后台线程数量的场景。
    • 使用 DelayQueue 作为任务队列。
  • SingleThreadScheduledExecutor
    • 只包含一个线程的 ScheduledThreadPoolExecutor,适用于需要单个后台线程执行周期任务,并且需要保证顺序执行任务的场景。

Future 接口

  • Future 接口及其实现类 FutureTask 用来表示异步计算的结果。
  • FutureTask 根据其运行状态可以分为:
    1. 未启动FutureTask.run() 方法尚未执行。
    2. 已启动FutureTask.run() 方法正在执行。
    3. 已完成FutureTask.run() 方法执行完毕并正常结束。
  • FutureTask 处于未启动或已启动状态时,调用 FutureTask.get() 方法将导致调用线程阻塞;当 FutureTask 处于已完成状态时,调用 FutureTask.get() 方法将立即返回结果或抛出异常。
  • FutureTask.cancel() 方法的行为:
    • 在未启动状态下,调用 FutureTask.cancel() 方法将导致任务永远不会被执行。
    • 在已启动状态下,调用 FutureTask.cancel(true) 方法将中断执行任务的线程,调用 FutureTask.cancel(false) 方法则不会影响正在执行的任务线程。
    • 在已完成状态下,调用 FutureTask.cancel(...) 方法将返回 false

Runnable 接口 / Callable 接口

  • Runnable 接口不返回结果,而 Callable 接口可以返回结果。
  • 可以通过工厂类 Executors 将一个 Runnable 包装为一个 Callable

Executors

Executors 是一个工具类,提供了便捷的方法来创建各种类型的线程池,例如 SingleThreadExecutorFixedThreadPoolCachedThreadPool。这些方法简化了线程池的创建和管理,使得开发者可以轻松使用 Executor 框架处理并发任务。