80. executor 、task 和 stream 优先于线程

      到本书第二版出版的时候, Java 平台中已经增加了 。这个字包中包含了一个 Executor Framework 它是一个很灵活的基于接口的任务执行工具。它创建了一个在各方面都比本书第一版更好的工作队列,却只需要这一行代码:

      下面是为执行而提交一个 runnable 的方法:

      你可以利用 executor service 完成更多的工作。例如,可以等待完成一项特殊的任务(就如第 79 条中的 get 方法一样),你可以等待一个任务集合中的任何任务或者所有任务完成(利用 invokeAny 或者 invokeAll 方法),可以等待 executor service 优雅地完成终止(利用 awaitTermination 方法),可以在任务完成时逐个地获取这些任务的结果(利用 ExecutorCompletionService),可以调度在某个特殊的时间段定时运行或者阶段性地运行的任务(利用 ),等等。

      如果想让不止一个线程来处理来自这个队列的请求,只要调用一个不同的静态工厂,这个工厂创建了一种不同的 executor service ,称作线程池(thread pool ) 。你可以用固定或者可变数目的线程创建一个线程池。java.util.concurrent.Executors 类包含了静态工厂,能为你提供所需的大多数 executor 。然而,如果你想来点特别的,可以直接使用 ThreadPoolExecutor 类。这个类允许你控制线程池操作的几乎每个方面。

      不仅应该尽量不要编写自己的工作队列,而且还应该尽量不直接使用线程。当直接使用线程时, Thread 是既充当工作单元,又是执行机制。在 Executor Framework 中,工作单元和执行机制是分开的。现在关键的抽象是工作单元,称作任务(task) 。任务有两种:Runnable 及其近亲 Callable (它与 Runnable 类似,但它会返回值,并且能够抛出任意的异常)。执行任务的通用机制是 executor service 。如果你从任务的角度来看问题,并让一个 executor service 替你执行任务,在选择适当的执行策略方面就获得了极大的灵活性。本质上,Executor 框架执行的功能与 Collections 框架聚合(aggregation)的功能相同。

      在 Java 7 中, Executor 框架被扩展为支持 fork-join 任务,这些任务是通过一种称作 fork-join 池的特殊 executor 服务运行的。fork-join 任务用 ForkJoinTask 实例表示,可以被分成更小的子任务,包含 ForkJoinPool 的线程不仅要处理这些任务,还要从另一个线程中“偷”任务,以确保所有的线程保持忙碌,从而提高 CPU 使用率、提高吞吐量,并降低延迟。fork-join 任务的编写和调优是很有技巧的。并行流 Parallel streams (详见第 48 条)是在 fork join 池上编写的,我们不费什么力气就能享受到它们的性能优势,前提是假设它们正好适用于我们手边的任务。