美文网首页
ITEM 80: 使用 EXECUTORS, TASKS, 或

ITEM 80: 使用 EXECUTORS, TASKS, 或

作者: rabbittttt | 来源:发表于2020-08-19 23:22 被阅读0次

ITEM 80: PREFER EXECUTORS, TASKS, AND STREAMS TO THREADS
  这本书的第一版包含一个简单工作队列的代码[Bloch01,item 49]。这个类允许客户端通过后台线程将异步处理的工作排队。当工作队列不再需要时,客户端可以调用一个方法,要求后台线程在完成队列中已经存在的任何工作后优雅地终止自己。这个实现就像一个玩具,但即便如此,它也需要一页微妙、精致的代码,这种代码如果不正确,很容易导致安全性和活性失效。幸运的是,再也没有理由编写这类代码了。
  这本书的第二版出版的时候, java.util.concurrent 已经被添加到 Java 中。这个包包含一个Executor 框架,它是一个灵活的基于接口的任务执行工具。创建一个工作队列在任何方面都比在这本书的第一版中更好,只需要一行代码:
ExecutorService exec = Executors.newSingleThreadExecutor();
  下面是如何提交一个runnable执行:
exec.execute(runnable);
  下面是如何告诉执行程序优雅地终止(如果你做不到这一点,你的VM很可能不会退出):
exec.shutdown();
  您可以使用 Executor 做更多的事情。例如,你可以等待一个特定任务完成(使用 get 方法),你可以等待任何或全部任务完成的集合(使用 invokeAny 或 invokeAll 方法),您可以等待执行者服务终止(使用 awaitTermination 方法),您可以一个接一个地检索任务,因为他们完成的结果(使用ExecutorCompletionService),您可以安排任务在特定时间运行或定期运行(使用ScheduledThreadPoolExecutor) 等等。
  如果需要多个线程来处理来自队列的请求,只需调用另一个静态工厂,该工厂将创建称为线程池的另一种执行器服务。您可以创建具有固定或可变数量线程的线程池。
java.util.concurrent.Executors 类包含静态工厂,这些工厂提供了您需要的大多数 executor。但是,如果您想要一些不同寻常的东西,您可以直接使用 ThreadPoolExecutor 类。这个类允许您配置线程池的几乎所有方面。
  为特定应用程序选择 executor 服务可能比较棘手。对于一个小程序,或者一个轻负载的服务器,Executors.newCachedThreadPool 通常是一个不错的选择,因为它不需要配置,而且通常“做正确的事情”。但是缓存的线程池对于高负载的生产服务器不是一个好的选择!在缓存的线程池中,提交的任务不会排队,而是立即交给线程执行。如果没有可用的线程,则创建一个新的线程。如果服务器负载过重,所有cpu都被充分利用,并且到达的任务越多,就会创建更多的线程,这只会让事情变得更糟。因此,在高负载的生产服务器中,最好使用 Executors.newFixedThreadPool,它为您提供一个具有固定数目线程的池,或者直接使用ThreadPoolExecutor 类,以实现最大的控制。
  您不仅应该避免编写自己的工作队列,而且通常应该避免直接使用线程。当您直接使用线程时,线程既充当工作单元,又充当执行它的机制。在 executor 框架中,工作单元和执行机制是分开的。关键的抽象是工作单元,即任务。有两种类型的任务:Runnable 和它的近亲 Callable (它类似于 Runnable,只是它会返回一个值并抛出任意异常)。执行任务的一般机制是 executor 服务。如果您从任务的角度进行思考,并让 executor 服务为您执行它们,那么您就可以灵活地选择合适的执行策略来满足您的需求,并在需求发生变化时更改策略。本质上,Executor 框架执行的功能与集合框架执行聚合的功能相同。
  在 Java 7 中,Executor 框架被扩展为支持 fork-join 任务,这些任务由一种称为 fork-join 池的特殊类型的执行器服务运行。fork-join任务, 由 ForkJoinTask 实例表示,可能分成更小的子任务,以及线程组成 ForkJoinPool 不仅处理这些任务,还会“偷”任务从一个另一个,以确保所有线程保持忙碌,提高CPU利用率,使程序获得更高的吞吐量和更低的延迟。编写和调优 fork-join 任务是需要技巧的。并行流(item 48)是在 fork 连接池上编写的,它允许您通过很少的努力来利用它们的性能优势,假设它们适合当前的任务。
  对 Executor 框架的完整描述超出了本书的范围,但感兴趣的读者将关注实践中的 Java 并发性[Goetz06]。

相关文章

网友评论

      本文标题:ITEM 80: 使用 EXECUTORS, TASKS, 或

      本文链接:https://www.haomeiwen.com/subject/pfjjjktx.html